diff --git a/References/DelphiAST/Demo/Parser/ParserDemo.dpr b/References/DelphiAST/Demo/Parser/ParserDemo.dpr new file mode 100644 index 000000000..e092c9311 --- /dev/null +++ b/References/DelphiAST/Demo/Parser/ParserDemo.dpr @@ -0,0 +1,18 @@ +program ParserDemo; + +uses + FastMM4, + Forms, + uMainForm in 'uMainForm.pas' {MainForm}, + StringUsageLogging in 'StringUsageLogging.pas'; + +{$R *.res} + +begin + System.ReportMemoryLeaksOnShutdown := True; + + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/References/DelphiAST/Demo/Parser/ParserDemo.dproj b/References/DelphiAST/Demo/Parser/ParserDemo.dproj new file mode 100644 index 000000000..1c9df8989 --- /dev/null +++ b/References/DelphiAST/Demo/Parser/ParserDemo.dproj @@ -0,0 +1,547 @@ + + + {6DAA4B8F-6103-4418-BAA9-E92227FE34C9} + 18.2 + VCL + ParserDemo.dpr + True + Debug + Win32 + 1 + Application + + + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + ..\..\Source;..\..\Source\SimpleParser;$(DCC_UnitSearchPath) + ParserDemo + System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace) + $(BDS)\bin\default_app.manifest + 1049 + CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments= + $(BDS)\bin\delphi_PROJECTICON.ico + false + false + false + false + false + + + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + true + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + true + IndyIPClient;FireDACASADriver;FireDACSqliteDriver;bindcompfmx;FireDACDSDriver;DBXSqliteDriver;vcldbx;FireDACPgDriver;FireDACODBCDriver;RESTBackendComponents;fmx;rtl;dbrtl;DbxClientDriver;IndySystem;FireDACCommon;bindcomp;inetdb;tethering;inetdbbde;DBXInterBaseDriver;DataSnapClient;DataSnapServer;DataSnapCommon;DBXOdbcDriver;vclFireDAC;DataSnapProviderClient;xmlrtl;DataSnapNativeClient;DBXSybaseASEDriver;DbxCommonDriver;svnui;vclimg;IndyProtocols;dbxcds;DBXMySQLDriver;DatasnapConnectorsFreePascal;FireDACCommonDriver;MetropolisUILiveTile;bindcompdbx;bindengine;vclactnband;vcldb;soaprtl;vcldsnap;bindcompvcl;vclie;fmxFireDAC;FireDACADSDriver;DBXDb2Driver;vcltouch;DBXOracleDriver;CustomIPTransport;vclribbon;VclSmp;FireDACMSSQLDriver;FireDAC;dsnap;DBXInformixDriver;fmxase;vcl;IndyCore;IndyIPServer;DataSnapServerMidas;DBXMSSQLDriver;IndyIPCommon;VCLRESTComponents;dsnapcon;FireDACIBDriver;DBXFirebirdDriver;inet;CloudService;DataSnapFireDAC;fmxobj;DataSnapConnectors;FireDACDBXDriver;FireDACMySQLDriver;soapmidas;vclx;soapserver;inetdbxpress;CodeSiteExpressPkg;svn;DBXSybaseASADriver;dsnapxml;FireDACOracleDriver;FireDACInfxDriver;FireDACDb2Driver;fmxdae;RESTComponents;bdertl;FireDACMSAccDriver;dbexpress;DataSnapIndy10ServerTransport;adortl;$(DCC_UsePackage) + 1033 + + + DEBUG;$(DCC_Define) + true + false + true + true + true + + + true + Debug + true + 1033 + false + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + + MainSource + + +
MainForm
+
+ + + Cfg_2 + Base + + + Base + + + Cfg_1 + Base + +
+ + Delphi.Personality.12 + + + + + ParserDemo.dpr + + + Microsoft Office 2000 Sample Automation Server Wrapper Components + Microsoft Office XP Sample Automation Server Wrapper Components + + + + + + ParserDemo.exe + true + + + + + 1 + + + 1 + + + + + Contents\Resources + 1 + + + + + classes + 1 + + + + + res\drawable-xxhdpi + 1 + + + + + Contents\MacOS + 0 + + + 1 + + + Contents\MacOS + 1 + + + + + library\lib\mips + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 0 + + + 1 + + + Contents\MacOS + 1 + + + library\lib\armeabi-v7a + 1 + + + 1 + + + + + 0 + + + Contents\MacOS + 1 + .framework + + + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + 1 + + + 1 + + + + + library\lib\armeabi + 1 + + + + + 0 + + + 1 + + + Contents\MacOS + 1 + + + + + 1 + + + 1 + + + 1 + + + + + res\drawable-normal + 1 + + + + + res\drawable-xhdpi + 1 + + + + + res\drawable-large + 1 + + + + + 1 + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable-hdpi + 1 + + + + + Contents + 1 + + + + + ..\ + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + 1 + + + 1 + + + 1 + + + + + res\values + 1 + + + + + res\drawable-small + 1 + + + + + res\drawable + 1 + + + + + 1 + + + 1 + + + 1 + + + + + 1 + + + + + res\drawable + 1 + + + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + 0 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 0 + .bpl + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + + + res\drawable-mdpi + 1 + + + + + res\drawable-xlarge + 1 + + + + + res\drawable-ldpi + 1 + + + + + 0 + .dll;.bpl + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + + + + + + + + + + + True + + + 12 + + + + +
diff --git a/References/DelphiAST/Demo/Parser/ParserDemo.lpi b/References/DelphiAST/Demo/Parser/ParserDemo.lpi new file mode 100644 index 000000000..bcbe86d82 --- /dev/null +++ b/References/DelphiAST/Demo/Parser/ParserDemo.lpi @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <RequiredPackages Count="1"> + <Item1> + <PackageName Value="LCL"/> + </Item1> + </RequiredPackages> + <Units Count="4"> + <Unit0> + <Filename Value="ParserDemo.lpr"/> + <IsPartOfProject Value="True"/> + </Unit0> + <Unit1> + <Filename Value="uMainForm.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="MainForm"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + </Unit1> + <Unit2> + <Filename Value="..\..\Source\DelphiAST.Classes.pas"/> + <IsPartOfProject Value="True"/> + </Unit2> + <Unit3> + <Filename Value="..\..\Source\DelphiAST.pas"/> + <IsPartOfProject Value="True"/> + </Unit3> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <SearchPaths> + <IncludeFiles Value="..\..\Source;..\..\Source\SimpleParser;..\..\Source\FreePascalSupport\Generics.Collections\src;$(ProjOutDir)"/> + <OtherUnitFiles Value="..\..\Source;..\..\Source\FreePascalSupport\FPC_StringBuilder\Src;..\..\Source\FreePascalSupport\Generics.Collection\src;..\..\Source\FreePascalSupport;..\..\Source\SimpleParser"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + </SyntaxOptions> + </Parsing> + <Linking> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + <Other> + <CustomOptions Value="-dBorland -dVer150 -dDelphi7 -dCompiler6_Up -dPUREPASCAL"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Demo/Parser/ParserDemo.lpr b/References/DelphiAST/Demo/Parser/ParserDemo.lpr new file mode 100644 index 000000000..c55ff0a9c --- /dev/null +++ b/References/DelphiAST/Demo/Parser/ParserDemo.lpr @@ -0,0 +1,16 @@ +program ParserDemo; + +{$MODE Delphi} + +uses + Forms, Interfaces, + uMainForm in 'uMainForm.pas' {MainForm}; + +{$R *.res} + +begin + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TMainForm, MainForm); + Application.Run; +end. diff --git a/References/DelphiAST/Demo/Parser/ParserDemo.lps b/References/DelphiAST/Demo/Parser/ParserDemo.lps new file mode 100644 index 000000000..8735f63a5 --- /dev/null +++ b/References/DelphiAST/Demo/Parser/ParserDemo.lps @@ -0,0 +1,178 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectSession> + <PathDelim Value="\"/> + <Version Value="9"/> + <BuildModes Active="Default"/> + <Units Count="9"> + <Unit0> + <Filename Value="ParserDemo.lpr"/> + <IsPartOfProject Value="True"/> + <IsVisibleTab Value="True"/> + <CursorPos X="53" Y="16"/> + <UsageCount Value="33"/> + <Loaded Value="True"/> + <DefaultSyntaxHighlighter Value="Delphi"/> + </Unit0> + <Unit1> + <Filename Value="uMainForm.pas"/> + <IsPartOfProject Value="True"/> + <HasResources Value="True"/> + <EditorIndex Value="1"/> + <TopLine Value="22"/> + <CursorPos X="58" Y="48"/> + <UsageCount Value="33"/> + <Loaded Value="True"/> + <DefaultSyntaxHighlighter Value="Delphi"/> + </Unit1> + <Unit2> + <Filename Value="..\Source\DelphiAST.Classes.pas"/> + <IsPartOfProject Value="True"/> + <EditorIndex Value="2"/> + <TopLine Value="49"/> + <CursorPos X="29" Y="68"/> + <UsageCount Value="33"/> + <Loaded Value="True"/> + <DefaultSyntaxHighlighter Value="Delphi"/> + </Unit2> + <Unit3> + <Filename Value="..\Source\DelphiAST.pas"/> + <IsPartOfProject Value="True"/> + <EditorIndex Value="4"/> + <CursorPos X="22" Y="8"/> + <UsageCount Value="33"/> + <Loaded Value="True"/> + <DefaultSyntaxHighlighter Value="Delphi"/> + </Unit3> + <Unit4> + <Filename Value="..\Source\SimpleParser\SimpleParser.pas"/> + <EditorIndex Value="3"/> + <TopLine Value="684"/> + <CursorPos X="42" Y="661"/> + <UsageCount Value="16"/> + <Loaded Value="True"/> + <DefaultSyntaxHighlighter Value="Delphi"/> + </Unit4> + <Unit5> + <Filename Value="..\..\Generics.Collections\src\inc\generics.dictionaries.inc"/> + <EditorIndex Value="-1"/> + <TopLine Value="143"/> + <CursorPos X="92" Y="158"/> + <UsageCount Value="9"/> + <DefaultSyntaxHighlighter Value="Delphi"/> + </Unit5> + <Unit6> + <Filename Value="..\..\Generics.Collections\src\generics.defaults.pas"/> + <EditorIndex Value="-1"/> + <TopLine Value="59"/> + <CursorPos X="48" Y="85"/> + <UsageCount Value="15"/> + <DefaultSyntaxHighlighter Value="Delphi"/> + </Unit6> + <Unit7> + <Filename Value="..\Source\DelphiAST.Writer.pas"/> + <EditorIndex Value="5"/> + <TopLine Value="65"/> + <CursorPos X="15" Y="74"/> + <UsageCount Value="15"/> + <Loaded Value="True"/> + <DefaultSyntaxHighlighter Value="Delphi"/> + </Unit7> + <Unit8> + <Filename Value="..\..\FPC_StringBuilder\Src\StringBuilderUnit.pas"/> + <EditorIndex Value="-1"/> + <TopLine Value="98"/> + <CursorPos X="95" Y="124"/> + <UsageCount Value="14"/> + <DefaultSyntaxHighlighter Value="Delphi"/> + </Unit8> + </Units> + <JumpHistory Count="22" HistoryIndex="21"> + <Position1> + <Filename Value="..\Source\SimpleParser\SimpleParser.pas"/> + <Caret Line="702" Column="29" TopLine="675"/> + </Position1> + <Position2> + <Filename Value="..\Source\DelphiAST.Classes.pas"/> + </Position2> + <Position3> + <Filename Value="..\Source\DelphiAST.pas"/> + <Caret Line="1528" Column="14" TopLine="1492"/> + </Position3> + <Position4> + <Filename Value="..\Source\DelphiAST.pas"/> + <Caret Line="1518" Column="26" TopLine="1492"/> + </Position4> + <Position5> + <Filename Value="..\Source\DelphiAST.pas"/> + <Caret Line="231" Column="22" TopLine="188"/> + </Position5> + <Position6> + <Filename Value="..\Source\DelphiAST.pas"/> + <Caret Line="234" Column="34" TopLine="190"/> + </Position6> + <Position7> + <Filename Value="..\Source\DelphiAST.pas"/> + <Caret Line="216" Column="48" TopLine="198"/> + </Position7> + <Position8> + <Filename Value="..\Source\DelphiAST.pas"/> + <Caret Line="231" Column="49" TopLine="201"/> + </Position8> + <Position9> + <Filename Value="..\Source\DelphiAST.pas"/> + <Caret Line="52" Column="131" TopLine="31"/> + </Position9> + <Position10> + <Filename Value="..\Source\DelphiAST.Writer.pas"/> + </Position10> + <Position11> + <Filename Value="..\Source\DelphiAST.Writer.pas"/> + <Caret Line="7" Column="23"/> + </Position11> + <Position12> + <Filename Value="..\Source\DelphiAST.Writer.pas"/> + <Caret Line="22" Column="48"/> + </Position12> + <Position13> + <Filename Value="..\Source\SimpleParser\SimpleParser.pas"/> + <Caret Line="662" Column="35" TopLine="637"/> + </Position13> + <Position14> + <Filename Value="..\Source\SimpleParser\SimpleParser.pas"/> + <Caret Line="647" Column="34" TopLine="637"/> + </Position14> + <Position15> + <Filename Value="..\Source\SimpleParser\SimpleParser.pas"/> + <Caret Line="678" TopLine="637"/> + </Position15> + <Position16> + <Filename Value="..\Source\SimpleParser\SimpleParser.pas"/> + <Caret Line="677" Column="11" TopLine="637"/> + </Position16> + <Position17> + <Filename Value="..\Source\SimpleParser\SimpleParser.pas"/> + <Caret Line="676" TopLine="637"/> + </Position17> + <Position18> + <Filename Value="..\Source\SimpleParser\SimpleParser.pas"/> + <Caret Line="693" Column="58" TopLine="686"/> + </Position18> + <Position19> + <Filename Value="..\Source\SimpleParser\SimpleParser.pas"/> + <Caret Line="667" TopLine="646"/> + </Position19> + <Position20> + <Filename Value="..\Source\SimpleParser\SimpleParser.pas"/> + <Caret Line="676" Column="24" TopLine="648"/> + </Position20> + <Position21> + <Filename Value="..\Source\DelphiAST.pas"/> + <Caret Line="239" Column="8" TopLine="201"/> + </Position21> + <Position22> + <Filename Value="ParserDemo.lpr"/> + </Position22> + </JumpHistory> + </ProjectSession> +</CONFIG> diff --git a/References/DelphiAST/Demo/Parser/ParserDemo.or b/References/DelphiAST/Demo/Parser/ParserDemo.or new file mode 100644 index 000000000..682f18451 Binary files /dev/null and b/References/DelphiAST/Demo/Parser/ParserDemo.or differ diff --git a/References/DelphiAST/Demo/Parser/StringUsageLogging.pas b/References/DelphiAST/Demo/Parser/StringUsageLogging.pas new file mode 100644 index 000000000..29b5b50b2 --- /dev/null +++ b/References/DelphiAST/Demo/Parser/StringUsageLogging.pas @@ -0,0 +1,94 @@ +unit StringUsageLogging; + +interface + +uses + Generics.Collections; + +type + TStringUsage = TPair<string,Integer>; + +function LogStringUsage: TArray<TStringUsage>; +procedure LogStringUsageToFile(const fileName: string); + +implementation + +uses + Generics.Defaults, FastMM4, Math, Classes, SysUtils; + +type + PStrRec = ^StrRec; + StrRec = packed record + {$IF defined(CPU64BITS)} + _Padding: Integer; + {$IFEND} + codePage: Word; + elemSize: Word; + refCnt: Integer; + length: Integer; + end; + +procedure Callback(APBlock: Pointer; ABlockSize: NativeInt; AUserData: Pointer); +var + items: TDictionary<string,Integer>; + count: Integer; +begin + items := TDictionary<string,Integer>(AUserData); + if (DetectClassInstance(APBlock) = nil) + and (DetectStringData(APBlock, ABlockSize) = stUnicodeString) then + begin + items.TryGetValue(string(PByte(APBlock) + SizeOf(StrRec)), count); + items.AddOrSetValue(string(PByte(APBlock) + SizeOf(StrRec)), count + 1); + end; +end; + +function LogStringUsage: TArray<TStringUsage>; +var + items: TDictionary<string,Integer>; + comparer: TComparison<TStringUsage>; +begin + items := TDictionary<string,Integer>.Create; + try + WalkAllocatedBlocks(Callback, items); + Result := items.ToArray; + comparer := + function(const left, right: TStringUsage): Integer + begin + Result := -CompareValue(left.Value, right.Value); + end; + TArray.Sort<TStringUsage>(Result, IComparer<TStringUsage>(PPointer(@comparer)^)); + finally + items.Free; + end; +end; + +procedure LogStringUsageToFile(const fileName: string); +var + item: TStringUsage; + f: TFileStream; + b: TBytes; + overall: Int64; +begin + f := TFileStream.Create(fileName, fmCreate); + b := TEncoding.UTF8.GetPreamble; + f.Write(b[0], Length(b)); + try + overall := 0; + for item in LogStringUsage do + begin + if item.Value > 1 then + begin + b := TEncoding.UTF8.GetBytes(Format('%s x%d'#13#10,[item.Key, item.Value])); + f.Write(b[0], Length(b)); + + Inc(overall, (SizeOf(StrRec) + Length(item.Key) + 1) * item.Value); + end; + end; + b := TEncoding.UTF8.GetBytes(Format(#13#10'Overall memory wasted: %d KB'#13#10, [overall div 1024])); + f.Write(b[0], Length(b)); + finally + f.Free; + end; +end; + +end. diff --git a/References/DelphiAST/Demo/Parser/uMainForm.dfm b/References/DelphiAST/Demo/Parser/uMainForm.dfm new file mode 100644 index 000000000..0605e5311 --- /dev/null +++ b/References/DelphiAST/Demo/Parser/uMainForm.dfm @@ -0,0 +1,124 @@ +object MainForm: TMainForm + Left = 0 + Top = 0 + Caption = 'DelphiAST Parser Demo' + ClientHeight = 436 + ClientWidth = 666 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + Menu = MainMenu + OldCreateOrder = False + PixelsPerInch = 96 + TextHeight = 13 + object Splitter1: TSplitter + Left = 0 + Top = 291 + Width = 666 + Height = 3 + Cursor = crVSplit + Align = alTop + ExplicitTop = 41 + ExplicitWidth = 206 + end + object OutputMemo: TMemo + Left = 0 + Top = 41 + Width = 666 + Height = 250 + Align = alTop + ScrollBars = ssBoth + TabOrder = 0 + ExplicitTop = 33 + ExplicitHeight = 168 + end + object StatusBar: TStatusBar + Left = 0 + Top = 417 + Width = 666 + Height = 19 + Panels = < + item + Width = 50 + end> + ExplicitTop = 370 + end + object CheckBox1: TCheckBox + AlignWithMargins = True + Left = 3 + Top = 397 + Width = 660 + Height = 17 + Align = alBottom + Caption = + 'Use string interning for less memory consumption (has a minor im' + + 'pact on speed)' + TabOrder = 2 + ExplicitTop = 350 + end + object CommentsBox: TListBox + Left = 0 + Top = 335 + Width = 666 + Height = 59 + Align = alClient + ItemHeight = 13 + TabOrder = 3 + ExplicitTop = 288 + end + object Panel1: TPanel + Left = 0 + Top = 0 + Width = 666 + Height = 41 + Align = alTop + BevelOuter = bvNone + TabOrder = 4 + ExplicitLeft = 88 + ExplicitTop = 8 + ExplicitWidth = 185 + object Label1: TLabel + Left = 16 + Top = 14 + Width = 63 + Height = 13 + Caption = 'Syntax Tree:' + end + end + object Panel2: TPanel + Left = 0 + Top = 294 + Width = 666 + Height = 41 + Align = alTop + BevelOuter = bvNone + TabOrder = 5 + ExplicitLeft = 184 + ExplicitTop = 272 + ExplicitWidth = 185 + object Label2: TLabel + Left = 16 + Top = 14 + Width = 86 + Height = 13 + Caption = 'List of Comments:' + end + end + object MainMenu: TMainMenu + Left = 224 + Top = 96 + object OpenDelphiUnit1: TMenuItem + Caption = 'Open Delphi Unit...' + OnClick = OpenDelphiUnit1Click + end + end + object OpenDialog: TOpenDialog + Filter = 'Delphi Unit|*.pas|Delphi Package|*.dpk|Delphi Project|*.dpr' + Options = [ofHideReadOnly, ofPathMustExist, ofFileMustExist, ofEnableSizing] + Left = 272 + Top = 96 + end +end diff --git a/References/DelphiAST/Demo/Parser/uMainForm.lfm b/References/DelphiAST/Demo/Parser/uMainForm.lfm new file mode 100644 index 000000000..5d35aa45d --- /dev/null +++ b/References/DelphiAST/Demo/Parser/uMainForm.lfm @@ -0,0 +1,61 @@ +object MainForm: TMainForm + Left = 309 + Height = 389 + Top = 89 + Width = 666 + Caption = 'DelphiAST Demo' + ClientHeight = 369 + ClientWidth = 666 + Color = clBtnFace + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Menu = MainMenu + LCLVersion = '1.6.0.4' + object OutputMemo: TMemo + Left = 0 + Height = 346 + Top = 0 + Width = 666 + Align = alClient + ScrollBars = ssBoth + TabOrder = 0 + end + object StatusBar: TStatusBar + Left = 0 + Height = 23 + Top = 346 + Width = 666 + Panels = < + item + Text = 'TEST' + Width = 500 + end> + end + object CheckBox1: TCheckBox + AlignWithMargins = True + Left = 3 + Top = 350 + Width = 660 + Height = 17 + Align = alBottom + Caption = + 'Use string interning for less memory consumption (has a minor im' + + 'pact on speed)' + TabOrder = 2 + end + object MainMenu: TMainMenu + left = 224 + top = 96 + object OpenDelphiUnit1: TMenuItem + Caption = 'Open Delphi Unit...' + OnClick = OpenDelphiUnit1Click + end + end + object OpenDialog: TOpenDialog + Filter = 'Delphi Unit|*.pas|Delphi Package|*.dpk|Delphi Project|*.dpr' + Options = [ofHideReadOnly, ofPathMustExist, ofFileMustExist, ofEnableSizing] + left = 272 + top = 96 + end +end diff --git a/References/DelphiAST/Demo/Parser/uMainForm.pas b/References/DelphiAST/Demo/Parser/uMainForm.pas new file mode 100644 index 000000000..8b79b91a0 --- /dev/null +++ b/References/DelphiAST/Demo/Parser/uMainForm.pas @@ -0,0 +1,195 @@ +unit uMainForm; + +{$IFDEF FPC}{$MODE Delphi}{$ENDIF} + +interface + +uses + Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, + Dialogs, Menus, StdCtrls, ComCtrls, ExtCtrls; + +type + TMainForm = class(TForm) + OutputMemo: TMemo; + MainMenu: TMainMenu; + OpenDelphiUnit1: TMenuItem; + OpenDialog: TOpenDialog; + StatusBar: TStatusBar; + CheckBox1: TCheckBox; + CommentsBox: TListBox; + Panel1: TPanel; + Panel2: TPanel; + Splitter1: TSplitter; + Label1: TLabel; + Label2: TLabel; + procedure OpenDelphiUnit1Click(Sender: TObject); + private + procedure UpdateStatusBarText(const StatusText: string); + procedure Parse(const FileName: string; UseStringInterning: Boolean); + end; + +var + MainForm: TMainForm; + +implementation + +uses + {$IFNDEF FPC} + StringUsageLogging, FastMM4, + {$ENDIF} + StringPool, + DelphiAST, DelphiAST.Writer, DelphiAST.Classes, + SimpleParser.Lexer.Types, IOUtils, Diagnostics, + DelphiAST.SimpleParserEx; + +{$IFNDEF FPC} + {$R *.dfm} +{$ELSE} + {$R *.lfm} +{$ENDIF} + +type + TIncludeHandler = class(TInterfacedObject, IIncludeHandler) + private + FPath: string; + public + constructor Create(const Path: string); + function GetIncludeFileContent(const ParentFileName, IncludeName: string; + out Content: string; out FileName: string): Boolean; + end; + +{$IFNDEF FPC} +function MemoryUsed: Cardinal; + var + st: TMemoryManagerState; + sb: TSmallBlockTypeState; + begin + GetMemoryManagerState(st); + Result := st.TotalAllocatedMediumBlockSize + st.TotalAllocatedLargeBlockSize; + for sb in st.SmallBlockTypeStates do + Result := Result + sb.UseableBlockSize * sb.AllocatedBlockCount; +end; +{$ELSE} +function MemoryUsed: Cardinal; +begin + Result := GetFPCHeapStatus.CurrHeapUsed; +end; +{$ENDIF} + +procedure TMainForm.Parse(const FileName: string; UseStringInterning: Boolean); +var + SyntaxTree: TSyntaxNode; + memused: Cardinal; + sw: TStopwatch; + StringPool: TStringPool; + OnHandleString: TStringEvent; + Builder: TPasSyntaxTreeBuilder; + StringStream: TStringStream; + I: Integer; +begin + OutputMemo.Clear; + CommentsBox.Clear; + + try + if UseStringInterning then + begin + StringPool := TStringPool.Create; + OnHandleString := StringPool.StringIntern; + end + else + begin + StringPool := nil; + OnHandleString := nil; + end; + + memused := MemoryUsed; + sw := TStopwatch.StartNew; + try + Builder := TPasSyntaxTreeBuilder.Create; + try + StringStream := TStringStream.Create; + try + StringStream.LoadFromFile(FileName); + + Builder.IncludeHandler := TIncludeHandler.Create(ExtractFilePath(FileName)); + Builder.OnHandleString := OnHandleString; + StringStream.Position := 0; + + SyntaxTree := Builder.Run(StringStream); + try + OutputMemo.Lines.Text := TSyntaxTreeWriter.ToXML(SyntaxTree, True); + finally + SyntaxTree.Free; + end; + finally + StringStream.Free; + end; + + for I := 0 to Builder.Comments.Count - 1 do + CommentsBox.Items.Add(Format('[Line: %d, Col: %d] %s', + [Builder.Comments[I].Line, Builder.Comments[I].Col, Builder.Comments[I].Text])); + finally + Builder.Free; + end + finally + if UseStringInterning then + StringPool.Free; + end; + sw.Stop; + + UpdateStatusBarText(Format('Parsed file in %d ms - used memory: %d K', + [sw.ElapsedMilliseconds, (MemoryUsed - memused) div 1024])); + except + on E: ESyntaxTreeException do + OutputMemo.Lines.Text := Format('[%d, %d] %s', [E.Line, E.Col, E.Message]) + sLineBreak + sLineBreak + + TSyntaxTreeWriter.ToXML(E.SyntaxTree, True); + end; +end; + +procedure TMainForm.OpenDelphiUnit1Click(Sender: TObject); +begin + if OpenDialog.Execute then + Parse(OpenDialog.FileName, CheckBox1.Checked); +end; + +{ TIncludeHandler } + +constructor TIncludeHandler.Create(const Path: string); +begin + inherited Create; + FPath := Path; +end; + +function TIncludeHandler.GetIncludeFileContent(const ParentFileName, IncludeName: string; + out Content: string; out FileName: string): Boolean; +var + FileContent: TStringList; +begin + FileContent := TStringList.Create; + try + if not FileExists(TPath.Combine(FPath, IncludeName)) then + begin + Result := False; + Exit; + end; + + FileContent.LoadFromFile(TPath.Combine(FPath, IncludeName)); + Content := FileContent.Text; + FileName := TPath.Combine(FPath, IncludeName); + + Result := True; + finally + FileContent.Free; + end; +end; + +procedure TMainForm.UpdateStatusBarText(const StatusText: string); +begin + {$IFDEF FPC} + StatusBar.SimpleText:= StatusText; + {$ELSE} + StatusBar.Panels[0].Text := StatusText; + {$ENDIF} +end; + +end. diff --git a/References/DelphiAST/Demo/ProjectIndexer/ProjectIndexerResearch.dpr b/References/DelphiAST/Demo/ProjectIndexer/ProjectIndexerResearch.dpr new file mode 100644 index 000000000..1adde9532 --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/ProjectIndexerResearch.dpr @@ -0,0 +1,65 @@ +program ProjectIndexerResearch; + +{$APPTYPE CONSOLE} + +{$R *.res} + +uses + FastMM4, + System.SysUtils, + System.Generics.Collections, + DelphiAST.ProjectIndexer, + TestUnit in 'TestUnit.pas'; + +var + i : integer; + indexer: TProjectIndexer; + +begin + try + if ParamCount <> 1 then + Writeln(ParamStr(0) + ' <project.dpr>') + else begin + indexer := TProjectIndexer.Create; + try +// indexer.SearchPath := +// 'x:\common\pkg\dspack\src\DirectX9;x:\common\pkg\dspack\src\DSPack;x:\common\DCPCrypt2;x:\common\DCPCrypt2\Ciphers;x:\common\DCPCrypt2\Hashes;x:\common\EZDSL;x:\common\g32;x:\gp\common;' + +// 'x:\gp\common\except;x:\common\src;x:\common\iphlpapi;x:\common\jwa;x:\common\OmniXML;x:\common\OmniXML\extras;x:\ms\common;x:\common\MSSpell;x:\common\pkg\devexpress5\sources;' + +// 'x:\common\pkg\jcl\source\include;x:\common\pkg\jcl\source;x:\common\pkg\jcl\source\common;x:\common\pkg\jcl\source\windows;x:\common\pkg\jcl\source\vcl;x:\common\pkg\jcl\source\prototypes;' + +// 'x:\common\pkg\Abbrevia\source;x:\common\pkg\APRO\run;x:\common\pkg\ics\source;x:\common\pkg\ics\source\include;x:\common\pkg\ics\source\extras;x:\common\pkg\jvcl\archive;' + +// 'x:\common\pkg\svcom\AllVersions\DesignTime;x:\common\pkg\svcom\AllVersions\Runtime;x:\common\pkg\tsilang\units;x:\common\pkg\tsilang\units\Auxilary;x:\common\pkg\vt;x:\common\pkg\vt\common;' + +// 'x:\ms\hl\Delphi;x:\ms\hl\HASP;x:\common\pkg\btree;x:\ms\ettwin;x:\ms\hl\cdg;x:\ms\htdrv;x:\gp\dvb;x:\gp\sttdb3;x:\ms\termcom;x:\ln\Common;x:\ln\Decklink;x:\ln\SubtitleEmbedder;x:\ln\mxf;' + +// 'x:\ln\gxf;x:\ln\FABAudio;x:\gp\arcman;x:\ms\htdrv10;x:\common\fastmm;x:\gp\edl;x:\gp\edl\compile;x:\ln\TS;x:\ln\Renderer;x:\ln\DebugFilter;x:\common\pkg\ChantSpeechKit;x:\common\elevation;' + +// 'x:\ms\install;x:\common\omnithreadlibrary;x:\ln\VirtualStringTree;x:\ln\mpeg;x:\common\pkg\_FAB\Rtf98;x:\common\pkg\kbmMemTable;x:\common\pkg\zip;x:\ln\mp4;x:\common\pkg\sapi\demos\USBView;' + +// 'x:\common\pkg\AAF;x:\common\pkg\taskbarlist;x:\ms\hl\hasp;x:\common\pkg\TsiLang\Units;x:\common\pkg\TsiLang\Units\Auxilary;x:\common\pkg\htmlviewer\source;x:\common\pkg\ppdf;' + +// 'x:\common\pkg\DragDrop\Source;x:\common\pkg\DM\Source;x:\gp\utils;x:\common\pkg\TsiLang\Units;X:\common;x:\common\pkg\jvcl\run;x:\common\pkg\jvcl\common;x:\common\pkg\jvcl\resources;' + +// 'x:\ms\netapi;x:\ln\wm\source;x:\common\ribbon\lib;x:\common\ffmpeg;C:\Program Files (x86)\TestInsight\Source;x:\common\detours\src;x:\common\Spring4D\Source\Base;x:\common\Spring4D\Source\Base\Collections;' + +// 'x:\common\Spring4D\Source\Core\Interception'; +// indexer.Defines := 'DEBUG'; + indexer.SearchPath := 'sub2'; + indexer.Index(ParamStr(1)); + Writeln(indexer.ParsedUnits.Count, ' units'); + for i := 0 to indexer.ParsedUnits.Count - 1 do + Writeln(indexer.ParsedUnits[i].Name, ' in ', indexer.ParsedUnits[i].Path); + Writeln; + Writeln(indexer.IncludeFiles.Count, ' includes'); + for i := 0 to indexer.IncludeFiles.Count - 1 do + Writeln(indexer.IncludeFiles[i].Name, ' @ ', indexer.IncludeFiles[i].Path); + Writeln; + Writeln(indexer.NotFoundUnits.Count, ' not found'); + for i := 0 to indexer.NotFoundUnits.Count - 1 do + Writeln(indexer.NotFoundUnits[i]); + Writeln; + Writeln(indexer.Problems.Count, ' problems'); + for i := 0 to indexer.Problems.Count - 1 do + Writeln(Ord(indexer.Problems[i].ProblemType), ' ', indexer.Problems[i].FileName, ': ', + indexer.Problems[i].Description); + Write('>'); + Readln; + finally FreeAndNil(indexer); end; + end; + except + on E: Exception do + Writeln(E.ClassName, ': ', E.Message); + end; +end. diff --git a/References/DelphiAST/Demo/ProjectIndexer/ProjectIndexerResearch.dproj b/References/DelphiAST/Demo/ProjectIndexer/ProjectIndexerResearch.dproj new file mode 100644 index 000000000..e3b81dda5 --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/ProjectIndexerResearch.dproj @@ -0,0 +1,499 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <ProjectGuid>{CDE040EC-B3A5-4860-9B08-7B0867F04597}</ProjectGuid> + <MainSource>ProjectIndexerResearch.dpr</MainSource> + <Base>True</Base> + <Config Condition="'$(Config)'==''">Debug</Config> + <TargetedPlatforms>1</TargetedPlatforms> + <AppType>Console</AppType> + <FrameworkType>None</FrameworkType> + <ProjectVersion>18.2</ProjectVersion> + <Platform Condition="'$(Platform)'==''">Win32</Platform> + </PropertyGroup> + <PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''"> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''"> + <Base_Win32>true</Base_Win32> + <CfgParent>Base</CfgParent> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_1)'!=''"> + <Cfg_1>true</Cfg_1> + <CfgParent>Base</CfgParent> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_2)'!=''"> + <Cfg_2>true</Cfg_2> + <CfgParent>Base</CfgParent> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_2)'=='true') or '$(Cfg_2_Win32)'!=''"> + <Cfg_2_Win32>true</Cfg_2_Win32> + <CfgParent>Cfg_2</CfgParent> + <Cfg_2>true</Cfg_2> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="'$(Base)'!=''"> + <DCC_UnitSearchPath>..\..\source;..\..\source\simpleparser;$(DCC_UnitSearchPath)</DCC_UnitSearchPath> + <Icns_MainIcns>$(BDS)\bin\delphi_PROJECTICNS.icns</Icns_MainIcns> + <DCC_K>false</DCC_K> + <VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=;CFBundleName=</VerInfo_Keys> + <Icon_MainIcon>$(BDS)\bin\delphi_PROJECTICON.ico</Icon_MainIcon> + <DCC_N>false</DCC_N> + <SanitizedProjectName>ProjectIndexerResearch</SanitizedProjectName> + <DCC_E>false</DCC_E> + <DCC_F>false</DCC_F> + <DCC_S>false</DCC_S> + <VerInfo_Locale>1033</VerInfo_Locale> + <DCC_ImageBase>00400000</DCC_ImageBase> + <DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace)</DCC_Namespace> + </PropertyGroup> + <PropertyGroup Condition="'$(Base_Win32)'!=''"> + <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(ModuleName)</VerInfo_Keys> + <BT_BuildType>Debug</BT_BuildType> + <DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace> + <VerInfo_Locale>1033</VerInfo_Locale> + </PropertyGroup> + <PropertyGroup Condition="'$(Cfg_1)'!=''"> + <DCC_DebugInformation>0</DCC_DebugInformation> + <DCC_Define>RELEASE;$(DCC_Define)</DCC_Define> + <DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo> + <DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols> + </PropertyGroup> + <PropertyGroup Condition="'$(Cfg_2)'!=''"> + <DCC_GenerateStackFrames>true</DCC_GenerateStackFrames> + <DCC_Define>DEBUG;$(DCC_Define)</DCC_Define> + <DCC_Optimize>false</DCC_Optimize> + </PropertyGroup> + <PropertyGroup Condition="'$(Cfg_2_Win32)'!=''"> + <DCC_Define>FullDebugMode;$(DCC_Define)</DCC_Define> + <Debugger_RunParams>demo\DemoProject.dpr</Debugger_RunParams> + <Manifest_File>(None)</Manifest_File> + <VerInfo_Keys>CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments=;ProgramID=com.embarcadero.$(MSBuildProjectName)</VerInfo_Keys> + </PropertyGroup> + <ItemGroup> + <DelphiCompile Include="$(MainSource)"> + <MainSource>MainSource</MainSource> + </DelphiCompile> + <DCCReference Include="TestUnit.pas"/> + <BuildConfiguration Include="Debug"> + <Key>Cfg_2</Key> + <CfgParent>Base</CfgParent> + </BuildConfiguration> + <BuildConfiguration Include="Base"> + <Key>Base</Key> + </BuildConfiguration> + <BuildConfiguration Include="Release"> + <Key>Cfg_1</Key> + <CfgParent>Base</CfgParent> + </BuildConfiguration> + </ItemGroup> + <ProjectExtensions> + <Borland.Personality>Delphi.Personality.12</Borland.Personality> + <Borland.ProjectType/> + <BorlandProject> + <Delphi.Personality> + <Source> + <Source Name="MainSource">ProjectIndexerResearch.dpr</Source> + </Source> + <Excluded_Packages> + <Excluded_Packages Name="$(BDSBIN)\dcloffice2k240.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages> + <Excluded_Packages Name="$(BDSBIN)\dclofficexp240.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages> + </Excluded_Packages> + </Delphi.Personality> + <Platforms> + <Platform value="Win32">True</Platform> + </Platforms> + <Deployment Version="3"> + <DeployFile LocalName="ProjectIndexerResearch.exe" Configuration="Debug" Class="ProjectOutput"> + <Platform Name="Win32"> + <RemoteName>ProjectIndexerResearch.exe</RemoteName> + <Overwrite>true</Overwrite> + </Platform> + </DeployFile> + <DeployFile LocalName="$(BDS)\Redist\osx32\libcgunwind.1.0.dylib" Class="DependencyModule"> + <Platform Name="OSX32"> + <Overwrite>true</Overwrite> + </Platform> + </DeployFile> + <DeployFile LocalName="$(BDS)\Redist\iossimulator\libcgunwind.1.0.dylib" Class="DependencyModule"> + <Platform Name="iOSSimulator"> + <Overwrite>true</Overwrite> + </Platform> + </DeployFile> + <DeployFile LocalName="$(BDS)\Redist\iossimulator\libPCRE.dylib" Class="DependencyModule"> + <Platform Name="iOSSimulator"> + <Overwrite>true</Overwrite> + </Platform> + </DeployFile> + <DeployFile LocalName="$(BDS)\Redist\osx32\libcgsqlite3.dylib" Class="DependencyModule"> + <Platform Name="OSX32"> + <Overwrite>true</Overwrite> + </Platform> + </DeployFile> + <DeployClass Name="ProjectiOSDeviceResourceRules"/> + <DeployClass Name="ProjectOSXResource"> + <Platform Name="OSX32"> + <RemoteDir>Contents\Resources</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidClassesDexFile"> + <Platform Name="Android"> + <RemoteDir>classes</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AdditionalDebugSymbols"> + <Platform Name="Win32"> + <RemoteDir>Contents\MacOS</RemoteDir> + <Operation>0</Operation> + </Platform> + <Platform Name="OSX32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPad_Launch768"> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_LauncherIcon144"> + <Platform Name="Android"> + <RemoteDir>res\drawable-xxhdpi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidLibnativeMipsFile"> + <Platform Name="Android"> + <RemoteDir>library\lib\mips</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Required="true" Name="ProjectOutput"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + <Platform Name="Win32"> + <Operation>0</Operation> + </Platform> + <Platform Name="Linux64"> + <Operation>1</Operation> + </Platform> + <Platform Name="OSX32"> + <Operation>1</Operation> + </Platform> + <Platform Name="Android"> + <RemoteDir>library\lib\armeabi-v7a</RemoteDir> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="DependencyFramework"> + <Platform Name="Win32"> + <Operation>0</Operation> + </Platform> + <Platform Name="OSX32"> + <Operation>1</Operation> + <Extensions>.framework</Extensions> + </Platform> + </DeployClass> + <DeployClass Name="ProjectUWPManifest"> + <Platform Name="Win32"> + <Operation>1</Operation> + </Platform> + <Platform Name="Win64"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPhone_Launch640"> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPad_Launch1024"> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectiOSDeviceDebug"> + <Platform Name="iOSDevice64"> + <RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPhone_Launch320"> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectiOSInfoPList"/> + <DeployClass Name="AndroidLibnativeArmeabiFile"> + <Platform Name="Android"> + <RemoteDir>library\lib\armeabi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="DebugSymbols"> + <Platform Name="Win32"> + <Operation>0</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="OSX32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPad_Launch1536"> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_SplashImage470"> + <Platform Name="Android"> + <RemoteDir>res\drawable-normal</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_LauncherIcon96"> + <Platform Name="Android"> + <RemoteDir>res\drawable-xhdpi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_SplashImage640"> + <Platform Name="Android"> + <RemoteDir>res\drawable-large</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPhone_Launch640x1136"> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="UWP_DelphiLogo44"> + <Platform Name="Win32"> + <RemoteDir>Assets</RemoteDir> + <Operation>1</Operation> + </Platform> + <Platform Name="Win64"> + <RemoteDir>Assets</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectiOSEntitlements"/> + <DeployClass Name="Android_LauncherIcon72"> + <Platform Name="Android"> + <RemoteDir>res\drawable-hdpi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidGDBServer"> + <Platform Name="Android"> + <RemoteDir>library\lib\armeabi-v7a</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectOSXInfoPList"/> + <DeployClass Name="ProjectOSXEntitlements"/> + <DeployClass Name="UWP_DelphiLogo150"> + <Platform Name="Win32"> + <RemoteDir>Assets</RemoteDir> + <Operation>1</Operation> + </Platform> + <Platform Name="Win64"> + <RemoteDir>Assets</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPad_Launch2048"> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidSplashStyles"> + <Platform Name="Android"> + <RemoteDir>res\values</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_SplashImage426"> + <Platform Name="Android"> + <RemoteDir>res\drawable-small</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidSplashImageDef"> + <Platform Name="Android"> + <RemoteDir>res\drawable</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectiOSResource"> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectAndroidManifest"> + <Platform Name="Android"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_DefaultAppIcon"> + <Platform Name="Android"> + <RemoteDir>res\drawable</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="File"> + <Platform Name="Win32"> + <Operation>0</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>0</Operation> + </Platform> + <Platform Name="OSX32"> + <Operation>0</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>0</Operation> + </Platform> + <Platform Name="Android"> + <Operation>0</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>0</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidServiceOutput"> + <Platform Name="Android"> + <RemoteDir>library\lib\armeabi-v7a</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Required="true" Name="DependencyPackage"> + <Platform Name="Win32"> + <Operation>0</Operation> + <Extensions>.bpl</Extensions> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + <Platform Name="OSX32"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + </DeployClass> + <DeployClass Name="Android_LauncherIcon48"> + <Platform Name="Android"> + <RemoteDir>res\drawable-mdpi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_SplashImage960"> + <Platform Name="Android"> + <RemoteDir>res\drawable-xlarge</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_LauncherIcon36"> + <Platform Name="Android"> + <RemoteDir>res\drawable-ldpi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="DependencyModule"> + <Platform Name="Win32"> + <Operation>0</Operation> + <Extensions>.dll;.bpl</Extensions> + </Platform> + <Platform Name="OSX32"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + </DeployClass> + <ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/> + <ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/> + <ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/> + <ProjectRoot Platform="Linux64" Name="$(PROJECTNAME)"/> + <ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/> + <ProjectRoot Platform="OSX32" Name="$(PROJECTNAME)"/> + <ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/> + <ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/> + </Deployment> + </BorlandProject> + <ProjectFileVersion>12</ProjectFileVersion> + </ProjectExtensions> + <Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/> + <Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/> + <Import Project="$(MSBuildProjectName).deployproj" Condition="Exists('$(MSBuildProjectName).deployproj')"/> +</Project> diff --git a/References/DelphiAST/Demo/ProjectIndexer/TestUnit.pas b/References/DelphiAST/Demo/ProjectIndexer/TestUnit.pas new file mode 100644 index 000000000..087933421 --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/TestUnit.pas @@ -0,0 +1,17 @@ +unit TestUnit; + +interface + +type + TTestClass = class + procedure Test; export; + end; + +implementation + +procedure TTestClass.Test; +begin + +end; + +end. diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/DemoProject.dpr b/References/DelphiAST/Demo/ProjectIndexer/demo/DemoProject.dpr new file mode 100644 index 000000000..892eef337 --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/demo/DemoProject.dpr @@ -0,0 +1,27 @@ +program DemoProject; + +{$APPTYPE CONSOLE} + +{$R *.res} + +// Search path: sub2 + +uses + System.SysUtils, + Unit1 in 'sub1\Unit1.pas', + Unit2; +// UnitA; + +begin + try + Writeln(Unit1Folder); + Writeln(Unit1.unitfolder); + Writeln(Unit1FolderIndirect); + Writeln(Unit2.unitfolder); +// Writeln(UnitA.ID); + Readln; + except + on E: Exception do + Writeln(E.ClassName, ': ', E.Message); + end; +end. diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/DemoProject.otares b/References/DelphiAST/Demo/ProjectIndexer/demo/DemoProject.otares new file mode 100644 index 000000000..743599575 Binary files /dev/null and b/References/DelphiAST/Demo/ProjectIndexer/demo/DemoProject.otares differ diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/UnitAa.pas b/References/DelphiAST/Demo/ProjectIndexer/demo/UnitAa.pas new file mode 100644 index 000000000..e7c503a96 --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/demo/UnitAa.pas @@ -0,0 +1,10 @@ +unit UnitA; + +interface + +const + ID = 'A3'; + +implementation + +end. diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/sub1/Unit1.pas b/References/DelphiAST/Demo/ProjectIndexer/demo/sub1/Unit1.pas new file mode 100644 index 000000000..b9fa25c6e --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/demo/sub1/Unit1.pas @@ -0,0 +1,16 @@ +unit Unit1; + +interface + +uses + UnitA; + +{$I ..\sub1inc\include.inc} +{$I ..\subinc\include.inc} + +const + Unit1Folder = 'sub1:' + UnitA.ID; + +implementation + +end. diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/sub1/UnitA.pas b/References/DelphiAST/Demo/ProjectIndexer/demo/sub1/UnitA.pas new file mode 100644 index 000000000..0dc4323ef --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/demo/sub1/UnitA.pas @@ -0,0 +1,10 @@ +unit UnitA; + +interface + +const + ID = 'A1'; + +implementation + +end. diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/sub1inc/include.inc b/References/DelphiAST/Demo/ProjectIndexer/demo/sub1inc/include.inc new file mode 100644 index 000000000..759a24f17 --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/demo/sub1inc/include.inc @@ -0,0 +1,2 @@ +const + unitfolder = 'sub1inc'; \ No newline at end of file diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/sub2/Unit1.pas b/References/DelphiAST/Demo/ProjectIndexer/demo/sub2/Unit1.pas new file mode 100644 index 000000000..f6f56fc74 --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/demo/sub2/Unit1.pas @@ -0,0 +1,10 @@ +unit Unit1; + +interface + +const + Unit1Folder = 'sub2'; + +implementation + +end. diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/sub2/Unit2.pas b/References/DelphiAST/Demo/ProjectIndexer/demo/sub2/Unit2.pas new file mode 100644 index 000000000..14e5cdc45 --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/demo/sub2/Unit2.pas @@ -0,0 +1,21 @@ +unit Unit2; + +interface + +uses + Unit1, + UnitA; + +{$I ..\sub2inc\include.inc} +{$I ..\subinc\include.inc} + +function Unit1FolderIndirect: string; + +implementation + +function Unit1FolderIndirect: string; +begin + Result := Unit1Folder + ':' + UnitA.ID; +end; + +end. diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/sub2/UnitA.pas b/References/DelphiAST/Demo/ProjectIndexer/demo/sub2/UnitA.pas new file mode 100644 index 000000000..a759eeeae --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/demo/sub2/UnitA.pas @@ -0,0 +1,12 @@ +unit UnitA; + +interface + +const + ID = 'A2'; + +implementation + +{$I ..\sub2inc\include.inc} + +end. diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/sub2inc/include.inc b/References/DelphiAST/Demo/ProjectIndexer/demo/sub2inc/include.inc new file mode 100644 index 000000000..70d91daf3 --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/demo/sub2inc/include.inc @@ -0,0 +1,2 @@ +const + unitfolder = 'sub2inc'; \ No newline at end of file diff --git a/References/DelphiAST/Demo/ProjectIndexer/demo/subinc/include.inc b/References/DelphiAST/Demo/ProjectIndexer/demo/subinc/include.inc new file mode 100644 index 000000000..2829da9ef --- /dev/null +++ b/References/DelphiAST/Demo/ProjectIndexer/demo/subinc/include.inc @@ -0,0 +1,2 @@ +const + common = 'common'; diff --git a/References/DelphiAST/LICENSE b/References/DelphiAST/LICENSE new file mode 100644 index 000000000..14e2f777f --- /dev/null +++ b/References/DelphiAST/LICENSE @@ -0,0 +1,373 @@ +Mozilla Public License Version 2.0 +================================== + +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. diff --git a/References/DelphiAST/README.md b/References/DelphiAST/README.md new file mode 100644 index 000000000..6f9edc0dd --- /dev/null +++ b/References/DelphiAST/README.md @@ -0,0 +1,91 @@ +[![](https://tokei.rs/b1/github/RomanYankovsky/DelphiAST?category=lines)](https://github.com/RomanYankovsky/DelphiAST) [![](https://tokei.rs/b1/github/RomanYankovsky/DelphiAST?category=code)](https://github.com/RomanYankovsky/DelphiAST) [![](https://tokei.rs/b1/github/RomanYankovsky/DelphiAST?category=files)](https://github.com/RomanYankovsky/DelphiAST) +### Abstract Syntax Tree Builder for Delphi +With DelphiAST you can take real Delphi code and get an abstract syntax tree. One unit at time and without a symbol table though. + +FreePascal and Lazarus compatible. + +#### Sample input +```delphi +unit Unit1; + +interface + +uses + Unit2; + +function Sum(A, B: Integer): Integer; + +implementation + +function Sum(A, B: Integer): Integer; +begin + Result := A + B; +end; + +end. +``` + +#### Sample outcome +```xml +<UNIT line="1" col="1" name="Unit1"> + <INTERFACE begin_line="3" begin_col="1" end_line="10" end_col="1"> + <USES begin_line="5" begin_col="1" end_line="8" end_col="1"> + <UNIT line="6" col="3" name="Unit2"/> + </USES> + <METHOD begin_line="8" begin_col="1" end_line="10" end_col="1" kind="function" name="Sum"> + <PARAMETERS line="8" col="13"> + <PARAMETER line="8" col="14"> + <NAME line="8" col="14" value="A"/> + <TYPE line="8" col="20" name="Integer"/> + </PARAMETER> + <PARAMETER line="8" col="17"> + <NAME line="8" col="17" value="B"/> + <TYPE line="8" col="20" name="Integer"/> + </PARAMETER> + </PARAMETERS> + <RETURNTYPE line="8" col="30"> + <TYPE line="8" col="30" name="Integer"/> + </RETURNTYPE> + </METHOD> + </INTERFACE> + <IMPLEMENTATION begin_line="10" begin_col="1" end_line="17" end_col="1"> + <METHOD begin_line="12" begin_col="1" end_line="17" end_col="1" kind="function" name="Sum"> + <PARAMETERS line="12" col="13"> + <PARAMETER line="12" col="14"> + <NAME line="12" col="14" value="A"/> + <TYPE line="12" col="20" name="Integer"/> + </PARAMETER> + <PARAMETER line="12" col="17"> + <NAME line="12" col="17" value="B"/> + <TYPE line="12" col="20" name="Integer"/> + </PARAMETER> + </PARAMETERS> + <RETURNTYPE line="12" col="30"> + <TYPE line="12" col="30" name="Integer"/> + </RETURNTYPE> + <STATEMENTS begin_line="13" begin_col="1" end_line="15" end_col="4"> + <ASSIGN line="14" col="3"> + <LHS line="14" col="3"> + <IDENTIFIER line="14" col="3" name="Result"/> + </LHS> + <RHS line="14" col="13"> + <EXPRESSION line="14" col="13"> + <ADD line="14" col="15"> + <IDENTIFIER line="14" col="13" name="A"/> + <IDENTIFIER line="14" col="17" name="B"/> + </ADD> + </EXPRESSION> + </RHS> + </ASSIGN> + </STATEMENTS> + </METHOD> + </IMPLEMENTATION> +</UNIT> +``` + +#### Copyright +Copyright (c) 2014-2020 Roman Yankovsky (roman@yankovsky.me) et al + +DelphiAST is released under the Mozilla Public License, v. 2.0 + +See LICENSE for details. diff --git a/References/DelphiAST/Source/DelphiAST.Classes.pas b/References/DelphiAST/Source/DelphiAST.Classes.pas new file mode 100644 index 000000000..0da6f4978 --- /dev/null +++ b/References/DelphiAST/Source/DelphiAST.Classes.pas @@ -0,0 +1,587 @@ +unit DelphiAST.Classes; + +{$IFDEF FPC}{$MODE DELPHI}{$ENDIF} + +interface + +uses + SysUtils, Generics.Collections, SimpleParser.Lexer.Types, DelphiAST.Consts; + +type + EParserException = class(Exception) + strict private + FFileName: string; + FLine, FCol: Integer; + public + constructor Create(Line, Col: Integer; const FileName, Msg: string); reintroduce; + + property FileName: string read FFileName; + property Line: Integer read FLine; + property Col: Integer read FCol; + end; + + TAttributeEntry = TPair<TAttributeName, string>; + PAttributeEntry = ^TAttributeEntry; + + TSyntaxNodeClass = class of TSyntaxNode; + TSyntaxNode = class + private + FLineSeq: Integer; + FCol: Integer; + FLine: Integer; + FFileName: string; + function GetHasChildren: Boolean; + function GetHasAttributes: Boolean; + function TryGetAttributeEntry(const Key: TAttributeName; var AttributeEntry: PAttributeEntry): boolean; + protected + FAttributes: TArray<TAttributeEntry>; + FChildNodes: TArray<TSyntaxNode>; + FTyp: TSyntaxNodeType; + FParentNode: TSyntaxNode; + public + constructor Create(Typ: TSyntaxNodeType); + destructor Destroy; override; + + function Clone: TSyntaxNode; virtual; + procedure AssignPositionFrom(const Node: TSyntaxNode); + + function GetAttribute(const Key: TAttributeName): string; + function HasAttribute(const Key: TAttributeName): Boolean; + procedure SetAttribute(const Key: TAttributeName; const Value: string); + procedure ClearAttributes; + + function AddChild(Node: TSyntaxNode): TSyntaxNode; overload; + function AddChild(Typ: TSyntaxNodeType): TSyntaxNode; overload; + procedure DeleteChild(Node: TSyntaxNode); + procedure ExtractChild(Node: TSyntaxNode); + function FindNode(Typ: TSyntaxNodeType): TSyntaxNode; overload; + // Searches for a node located along the path from the type of nodes + // specified in the TypesPath parameter. + // ntUnknown in the TypesPath parameter means a node of any type. + // For example, for the branch presented below as XML + // FindNode([ntAbsolute, ntValue, ntExpression, ntIdentifier]), + // FindNode([ntAbsolute, ntUnknown, ntExpression, ntIdentifier]) è + // FindNode([ntAbsolute, ntUnknown, ntUnknown, ntIdentifier]) + // return the IDENTIFIER node. + // <VARIABLE line="9" col="3"> + // <NAME line="9" col="3" value="ValueRec"/> + // <TYPE line="9" col="13" name="LongInt"/> + // <ABSOLUTE line="9" col="21"> + // <VALUE line="9" col="30"> + // <EXPRESSION line="9" col="30"> + // <IDENTIFIER line="9" col="30" name="AValue"/> + // </EXPRESSION> + // </VALUE> + // </ABSOLUTE> + // </VARIABLE>. + function FindNode(const TypesPath: array of TSyntaxNodeType): TSyntaxNode; overload; + property Attributes: TArray<TAttributeEntry> read FAttributes; + property ChildNodes: TArray<TSyntaxNode> read FChildNodes; + property HasAttributes: Boolean read GetHasAttributes; + property HasChildren: Boolean read GetHasChildren; + property Typ: TSyntaxNodeType read FTyp; + property ParentNode: TSyntaxNode read FParentNode; + + property LineSeq: Integer read FLineSeq write FLineSeq; + property Col: Integer read FCol write FCol; + property Line: Integer read FLine write FLine; + property FileName: string read FFileName write FFileName; + end; + + TCompoundSyntaxNode = class(TSyntaxNode) + private + FEndCol: Integer; + FEndLine: Integer; + public + function Clone: TSyntaxNode; override; + + property EndCol: Integer read FEndCol write FEndCol; + property EndLine: Integer read FEndLine write FEndLine; + end; + + TValuedSyntaxNode = class(TSyntaxNode) + private + FValue: string; + public + function Clone: TSyntaxNode; override; + + property Value: string read FValue write FValue; + end; + + TCommentNode = class(TSyntaxNode) + private + FText: string; + public + function Clone: TSyntaxNode; override; + + property Text: string read FText write FText; + end; + + TExpressionTools = class + private + class function CreateNodeWithParentsPosition(NodeType: TSyntaxNodeType; ParentNode: TSyntaxNode): TSyntaxNode; + public + class function ExprToReverseNotation(Expr: TList<TSyntaxNode>): TList<TSyntaxNode>; static; + class procedure NodeListToTree(Expr: TList<TSyntaxNode>; Root: TSyntaxNode); static; + class function PrepareExpr(ExprNodes: TList<TSyntaxNode>): TList<TSyntaxNode>; static; + class procedure RawNodeListToTree(RawParentNode: TSyntaxNode; RawNodeList: TList<TSyntaxNode>; NewRoot: TSyntaxNode); static; + end; + +implementation + +type + TOperatorKind = (okUnary, okBinary); + TOperatorAssocType = (atLeft, atRight); + + TOperatorInfo = record + Typ: TSyntaxNodeType; + Priority: Byte; + Kind: TOperatorKind; + AssocType: TOperatorAssocType; + end; + + TOperators = class + strict private + class function GetItem(Typ: TSyntaxNodeType): TOperatorInfo; static; + public + class function IsOpName(Typ: TSyntaxNodeType): Boolean; + class property Items[Typ: TSyntaxNodeType]: TOperatorInfo read GetItem; default; + end; + +const + OperatorsInfo: array [0..29] of TOperatorInfo = + ((Typ: ntAddr; Priority: 1; Kind: okUnary; AssocType: atRight), + (Typ: ntDeref; Priority: 1; Kind: okUnary; AssocType: atLeft), + (Typ: ntGeneric; Priority: 1; Kind: okBinary; AssocType: atRight), + (Typ: ntIndexed; Priority: 1; Kind: okUnary; AssocType: atLeft), + (Typ: ntDot; Priority: 2; Kind: okBinary; AssocType: atRight), + (Typ: ntCall; Priority: 3; Kind: okBinary; AssocType: atRight), + (Typ: ntUnaryMinus; Priority: 5; Kind: okUnary; AssocType: atRight), + (Typ: ntNot; Priority: 6; Kind: okUnary; AssocType: atRight), + (Typ: ntMul; Priority: 7; Kind: okBinary; AssocType: atRight), + (Typ: ntFDiv; Priority: 7; Kind: okBinary; AssocType: atRight), + (Typ: ntDiv; Priority: 7; Kind: okBinary; AssocType: atRight), + (Typ: ntMod; Priority: 7; Kind: okBinary; AssocType: atRight), + (Typ: ntAnd; Priority: 7; Kind: okBinary; AssocType: atRight), + (Typ: ntShl; Priority: 7; Kind: okBinary; AssocType: atRight), + (Typ: ntShr; Priority: 7; Kind: okBinary; AssocType: atRight), + (Typ: ntAs; Priority: 7; Kind: okBinary; AssocType: atRight), + (Typ: ntAdd; Priority: 8; Kind: okBinary; AssocType: atRight), + (Typ: ntSub; Priority: 8; Kind: okBinary; AssocType: atRight), + (Typ: ntOr; Priority: 8; Kind: okBinary; AssocType: atRight), + (Typ: ntXor; Priority: 8; Kind: okBinary; AssocType: atRight), + (Typ: ntEqual; Priority: 9; Kind: okBinary; AssocType: atRight), + (Typ: ntNotEqual; Priority: 9; Kind: okBinary; AssocType: atRight), + (Typ: ntLower; Priority: 9; Kind: okBinary; AssocType: atRight), + (Typ: ntGreater; Priority: 9; Kind: okBinary; AssocType: atRight), + (Typ: ntLowerEqual; Priority: 9; Kind: okBinary; AssocType: atRight), + (Typ: ntGreaterEqual; Priority: 9; Kind: okBinary; AssocType: atRight), + (Typ: ntIn; Priority: 9; Kind: okBinary; AssocType: atRight), + (Typ: ntNotIn; Priority: 9; Kind: okBinary; AssocType: atRight), + (Typ: ntIs; Priority: 9; Kind: okBinary; AssocType: atRight), + (Typ: ntIsNot; Priority: 9; Kind: okBinary; AssocType: atRight)); + +{ TOperators } + +class function TOperators.GetItem(Typ: TSyntaxNodeType): TOperatorInfo; +var + i: Integer; +begin + for i := 0 to High(OperatorsInfo) do + if OperatorsInfo[i].Typ = Typ then + Exit(OperatorsInfo[i]); +end; + +class function TOperators.IsOpName(Typ: TSyntaxNodeType): Boolean; +var + i: Integer; +begin + for i := 0 to High(OperatorsInfo) do + if OperatorsInfo[i].Typ = Typ then + Exit(True); + Result := False; +end; + +function IsRoundClose(Typ: TSyntaxNodeType): Boolean; inline; +begin + Result := Typ = ntRoundClose; +end; + +function IsRoundOpen(Typ: TSyntaxNodeType): Boolean; inline; +begin + Result := Typ = ntRoundOpen; +end; + +class function TExpressionTools.ExprToReverseNotation(Expr: TList<TSyntaxNode>): TList<TSyntaxNode>; +var + Stack: TStack<TSyntaxNode>; + Node: TSyntaxNode; +begin + Result := TList<TSyntaxNode>.Create; + try + Stack := TStack<TSyntaxNode>.Create; + try + for Node in Expr do + if TOperators.IsOpName(Node.Typ) then + begin + while (Stack.Count > 0) and TOperators.IsOpName(Stack.Peek.Typ) and + (((TOperators.Items[Node.Typ].AssocType = atLeft) and + (TOperators.Items[Node.Typ].Priority >= TOperators.Items[Stack.Peek.Typ].Priority)) + or + ((TOperators.Items[Node.Typ].AssocType = atRight) and + (TOperators.Items[Node.Typ].Priority > TOperators.Items[Stack.Peek.Typ].Priority))) + do + Result.Add(Stack.Pop); + + Stack.Push(Node); + end + else if IsRoundOpen(Node.Typ) then + Stack.Push(Node) + else if IsRoundClose(Node.Typ) then + begin + while not IsRoundOpen(Stack.Peek.Typ) do + Result.Add(Stack.Pop); + + // RoundOpen and RoundClose nodes are not needed anymore + Stack.Pop.Free; + Node.Free; + + if (Stack.Count > 0) and TOperators.IsOpName(Stack.Peek.Typ) then + Result.Add(Stack.Pop); + end else + Result.Add(Node); + + while Stack.Count > 0 do + Result.Add(Stack.Pop); + finally + Stack.Free; + end; + except + FreeAndNil(Result); + raise; + end; +end; + +class procedure TExpressionTools.NodeListToTree(Expr: TList<TSyntaxNode>; Root: TSyntaxNode); +var + Stack: TStack<TSyntaxNode>; + Node, SecondNode: TSyntaxNode; +begin + Stack := TStack<TSyntaxNode>.Create; + try + for Node in Expr do + begin + if TOperators.IsOpName(Node.Typ) then + case TOperators.Items[Node.Typ].Kind of + okUnary: Node.AddChild(Stack.Pop); + okBinary: + begin + SecondNode := Stack.Pop; + Node.AddChild(Stack.Pop); + Node.AddChild(SecondNode); + end; + end; + Stack.Push(Node); + end; + + Root.AddChild(Stack.Pop); + + Assert(Stack.Count = 0); + finally + Stack.Free; + end; +end; + +class function TExpressionTools.PrepareExpr(ExprNodes: TList<TSyntaxNode>): TList<TSyntaxNode>; +var + Node, PrevNode: TSyntaxNode; +begin + Result := TList<TSyntaxNode>.Create; + try + Result.Capacity := ExprNodes.Count * 2; + + PrevNode := nil; + for Node in ExprNodes do + begin + if Node.Typ = ntCall then + Continue; + + if Assigned(PrevNode) and IsRoundOpen(Node.Typ) then + begin + if not TOperators.IsOpName(PrevNode.Typ) and not IsRoundOpen(PrevNode.Typ) then + Result.Add(CreateNodeWithParentsPosition(ntCall, Node.ParentNode)); + + if TOperators.IsOpName(PrevNode.Typ) + and (TOperators.Items[PrevNode.Typ].Kind = okUnary) + and (TOperators.Items[PrevNode.Typ].AssocType = atLeft) + then + Result.Add(CreateNodeWithParentsPosition(ntCall, Node.ParentNode)); + end; + + if Assigned(PrevNode) and (Node.Typ = ntTypeArgs) then + begin + if not TOperators.IsOpName(PrevNode.Typ) and (PrevNode.Typ <> ntTypeArgs) then + Result.Add(CreateNodeWithParentsPosition(ntGeneric, Node.ParentNode)); + + if TOperators.IsOpName(PrevNode.Typ) + and (TOperators.Items[PrevNode.Typ].Kind = okUnary) + and (TOperators.Items[PrevNode.Typ].AssocType = atLeft) + then + Result.Add(CreateNodeWithParentsPosition(ntGeneric, Node.ParentNode)); + end; + + if Node.Typ <> ntAlignmentParam then + Result.Add(Node.Clone); + PrevNode := Node; + end; + except + FreeAndNil(Result); + raise; + end; +end; + +class function TExpressionTools.CreateNodeWithParentsPosition(NodeType: TSyntaxNodeType; ParentNode: TSyntaxNode): TSyntaxNode; +begin + Result := TSyntaxNode.Create(NodeType); + Result.AssignPositionFrom(ParentNode); +end; + +class procedure TExpressionTools.RawNodeListToTree(RawParentNode: TSyntaxNode; RawNodeList: TList<TSyntaxNode>; + NewRoot: TSyntaxNode); +var + PreparedNodeList, ReverseNodeList: TList<TSyntaxNode>; +begin + try + PreparedNodeList := PrepareExpr(RawNodeList); + try + ReverseNodeList := ExprToReverseNotation(PreparedNodeList); + try + NodeListToTree(ReverseNodeList, NewRoot); + finally + ReverseNodeList.Free; + end; + finally + PreparedNodeList.Free; + end; + except + on E: Exception do + raise EParserException.Create(NewRoot.Line, NewRoot.Col, NewRoot.FileName, E.Message); + end; +end; + +{ TSyntaxNode } + +procedure TSyntaxNode.SetAttribute(const Key: TAttributeName; const Value: string); +var + AttributeEntry: PAttributeEntry; + len: Integer; +begin + if not TryGetAttributeEntry(Key, AttributeEntry) then + begin + len := Length(FAttributes); + SetLength(FAttributes, len + 1); + AttributeEntry := @FAttributes[len]; + AttributeEntry^.Key := Key; + end; + AttributeEntry^.Value := Value; +end; + +function TSyntaxNode.TryGetAttributeEntry(const Key: TAttributeName; var AttributeEntry: PAttributeEntry): boolean; +var + i: integer; +begin + for i := 0 to High(FAttributes) do + if FAttributes[i].Key = Key then + begin + AttributeEntry := @FAttributes[i]; + Exit(True); + end; + + Result := False; +end; + +function TSyntaxNode.AddChild(Node: TSyntaxNode): TSyntaxNode; +begin + Assert(Assigned(Node)); + + SetLength(FChildNodes, Length(FChildNodes) + 1); + FChildNodes[Length(FChildNodes) - 1] := Node; + + Node.FParentNode := Self; + + Result := Node; +end; + +function TSyntaxNode.AddChild(Typ: TSyntaxNodeType): TSyntaxNode; +begin + Result := AddChild(TSyntaxNode.Create(Typ)); +end; + +function TSyntaxNode.Clone: TSyntaxNode; +var + i: Integer; +begin + Result := TSyntaxNodeClass(Self.ClassType).Create(FTyp); + + SetLength(Result.FChildNodes, Length(FChildNodes)); + for i := 0 to High(FChildNodes) do + begin + Result.FChildNodes[i] := FChildNodes[i].Clone; + Result.FChildNodes[i].FParentNode := Result; + end; + + Result.FAttributes := Copy(FAttributes); + Result.AssignPositionFrom(Self); +end; + +constructor TSyntaxNode.Create(Typ: TSyntaxNodeType); +begin + inherited Create; + FTyp := Typ; +end; + +procedure TSyntaxNode.ExtractChild(Node: TSyntaxNode); +var + i: integer; +begin + for i := 0 to High(FChildNodes) do + if FChildNodes[i] = Node then + begin + if i < High(FChildNodes) then + Move(FChildNodes[i + 1], FChildNodes[i], SizeOf(TSyntaxNode) * (Length(FChildNodes) - i - 1)); + SetLength(FChildNodes, High(FChildNodes)); + Break; + end; +end; + +procedure TSyntaxNode.DeleteChild(Node: TSyntaxNode); +begin + ExtractChild(Node); + Node.Free; +end; + +destructor TSyntaxNode.Destroy; +var + i: integer; +begin + for i := 0 to High(FChildNodes) do + FreeAndNil(FChildNodes[i]); + inherited; +end; + +function TSyntaxNode.FindNode(Typ: TSyntaxNodeType): TSyntaxNode; +var + i: Integer; +begin + for i := 0 to High(FChildNodes) do + if FChildNodes[i].Typ = Typ then + Exit(FChildNodes[i]); + Result := nil; +end; + +function TSyntaxNode.FindNode(const TypesPath: array of TSyntaxNodeType): TSyntaxNode; + + function FindNodeRecursively(Node: TSyntaxNode; + const TypesPath: array of TSyntaxNodeType; TypeIndex: Integer): TSyntaxNode; + var + ChildNode: TSyntaxNode; + begin + Result := nil; + for ChildNode in Node.ChildNodes do + if TypesPath[TypeIndex] in [ChildNode.Typ] + [ntUnknown] then + begin + if TypeIndex < High(TypesPath) then + Result := FindNodeRecursively(ChildNode, TypesPath, TypeIndex + 1) + else + Result := ChildNode; + if Assigned(Result) then + Exit; + end; + end; + +begin + if TypesPath[High(TypesPath)] <> ntUnknown then + Result := FindNodeRecursively(Self, TypesPath, Low(TypesPath)) + else + Result := nil; +end; + +function TSyntaxNode.GetAttribute(const Key: TAttributeName): string; +var + AttributeEntry: PAttributeEntry; +begin + if TryGetAttributeEntry(Key, AttributeEntry) then + Result := AttributeEntry^.Value + else + Result := ''; +end; + +function TSyntaxNode.GetHasAttributes: Boolean; +begin + Result := Length(FAttributes) > 0; +end; + +function TSyntaxNode.GetHasChildren: Boolean; +begin + Result := Length(FChildNodes) > 0; +end; + +function TSyntaxNode.HasAttribute(const Key: TAttributeName): Boolean; +var + AttributeEntry: PAttributeEntry; +begin + Result := TryGetAttributeEntry(Key, AttributeEntry); +end; + +procedure TSyntaxNode.ClearAttributes; +begin + SetLength(FAttributes, 0); +end; + +procedure TSyntaxNode.AssignPositionFrom(const Node: TSyntaxNode); +begin + FLineSeq := Node.LineSeq; + FCol := Node.Col; + FLine := Node.Line; + FFileName := Node.FileName; +end; + +{ TCompoundSyntaxNode } + +function TCompoundSyntaxNode.Clone: TSyntaxNode; +begin + Result := inherited; + + TCompoundSyntaxNode(Result).EndLine := Self.EndLine; + TCompoundSyntaxNode(Result).EndCol := Self.EndCol; +end; + +{ TValuedSyntaxNode } + +function TValuedSyntaxNode.Clone: TSyntaxNode; +begin + Result := inherited; + + TValuedSyntaxNode(Result).Value := Self.Value; +end; + +{ TCommentNode } + +function TCommentNode.Clone: TSyntaxNode; +begin + Result := inherited; + + TCommentNode(Result).Text := Self.Text; +end; + +{ EParserException } + +constructor EParserException.Create(Line, Col: Integer; const FileName, Msg: string); +begin + inherited Create(Msg); + FFileName := FileName; + FLine := Line; + FCol := Col; +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Source/DelphiAST.Consts.pas b/References/DelphiAST/Source/DelphiAST.Consts.pas new file mode 100644 index 000000000..13522c037 --- /dev/null +++ b/References/DelphiAST/Source/DelphiAST.Consts.pas @@ -0,0 +1,323 @@ +unit DelphiAST.Consts; + +interface + +type + TSyntaxNodeType = ( + ntUnknown, + ntAbsolute, + ntAdd, + ntAddr, + ntAlignmentParam, + ntAnd, + ntAnonymousMethod, + ntArguments, + ntAs, + ntAssign, + ntAt, + ntAttribute, + ntAttributes, + ntBounds, + ntCall, + ntCase, + ntCaseElse, + ntCaseLabel, + ntCaseLabels, + ntCaseSelector, + ntClassConstraint, + ntConstant, + ntConstants, + ntConstraints, + ntConstructorConstraint, + ntContains, + ntDefault, + ntDeref, + ntDimension, + ntDiv, + ntDot, + ntDownTo, + ntElement, + ntElse, + ntEmptyStatement, + ntEnum, + ntEqual, + ntExcept, + ntExceptionHandler, + ntExports, + ntExpression, + ntExpressions, + ntExternal, + ntFDiv, + ntField, + ntFields, + ntFinalization, + ntFinally, + ntFor, + ntFrom, + ntGeneric, + ntGoto, + ntGreater, + ntGreaterEqual, + ntGuid, + ntHelper, + ntIdentifier, + ntIf, + ntImplementation, + ntImplements, + ntIn, + ntIndex, + ntIndexed, + ntInherited, + ntInitialization, + ntInterface, + ntIs, + ntIsNot, + ntLabel, + ntLHS, + ntLiteral, + ntLower, + ntLowerEqual, + ntMessage, + ntMethod, + ntMod, + ntMul, + ntName, + ntNamedArgument, + ntNotEqual, + ntNot, + ntNotIn, + ntOr, + ntPackage, + ntParameter, + ntParameters, + ntPath, + ntPositionalArgument, + ntProtected, + ntPrivate, + ntProperty, + ntPublic, + ntPublished, + ntRaise, + ntRead, + ntRecordConstraint, + ntRepeat, + ntRequires, + ntResolutionClause, + ntResourceString, + ntReturnType, + ntRHS, + ntRoundClose, + ntRoundOpen, + ntSet, + ntShl, + ntShr, + ntStatement, + ntStatements, + ntStrictPrivate, + ntStrictProtected, + ntSub, + ntSubrange, + ntTernaryOp, + ntThen, + ntTo, + ntTry, + ntType, + ntTypeArgs, + ntTypeDecl, + ntTypeParam, + ntTypeParams, + ntTypeSection, + ntValue, + ntVariable, + ntVariables, + ntXor, + ntUnaryMinus, + ntUnit, + ntUses, + ntWhile, + ntWith, + ntWrite, + + ntAnsiComment, + ntBorComment, + ntSlashesComment + ); + + TAttributeName = ( + anType, + anClass, + anForwarded, + anKind, + anName, + anVisibility, + anCallingConvention, + anPath, + anMethodBinding, + anReintroduce, + anOverload, + anAbstract, + anInline, + anAlign + ); + +const + SyntaxNodeNames: array [TSyntaxNodeType] of string = ( + 'unknown', + 'absolute', + 'add', + 'addr', + 'alignmentparam', + 'and', + 'anonymousmethod', + 'arguments', + 'as', + 'assign', + 'at', + 'attribute', + 'attributes', + 'bounds', + 'call', + 'case', + 'caseelse', + 'caselabel', + 'caselabels', + 'caseselector', + 'classconstraint', + 'constant', + 'constants', + 'constraints', + 'constructorconstraint', + 'contains', + 'default', + 'deref', + 'dimension', + 'div', + 'dot', + 'downto', + 'element', + 'else', + 'emptystatement', + 'enum', + 'equal', + 'except', + 'exceptionhandler', + 'exports', + 'expression', + 'expressions', + 'external', + 'fdiv', + 'field', + 'fields', + 'finalization', + 'finally', + 'for', + 'from', + 'generic', + 'goto', + 'greater', + 'greaterequal', + 'guid', + 'helper', + 'identifier', + 'if', + 'implementation', + 'implements', + 'in', + 'index', + 'indexed', + 'inherited', + 'initialization', + 'interface', + 'is', + 'isnot', + 'label', + 'lhs', + 'literal', + 'lower', + 'lowerequal', + 'message', + 'method', + 'mod', + 'mul', + 'name', + 'namedargument', + 'notequal', + 'not', + 'notin', + 'or', + 'package', + 'parameter', + 'parameters', + 'path', + 'positionalargument', + 'protected', + 'private', + 'property', + 'public', + 'published', + 'raise', + 'read', + 'recordconstraint', + 'repeat', + 'requires', + 'resolutionclause', + 'resourcestring', + 'returntype', + 'rhs', + 'roundclose', + 'roundopen', + 'set', + 'shl', + 'shr', + 'statement', + 'statements', + 'strictprivate', + 'strictprotected', + 'sub', + 'subrange', + 'ternaryop', + 'then', + 'to', + 'try', + 'type', + 'typeargs', + 'typedecl', + 'typeparam', + 'typeparams', + 'typesection', + 'value', + 'variable', + 'variables', + 'xor', + 'unaryminus', + 'unit', + 'uses', + 'while', + 'with', + 'write', + + 'ansicomment', + 'borlandcomment', + 'slashescomment' + ); + + AttributeNameStrings: array[TAttributeName] of string = ( + 'type', + 'class', + 'forwarded', + 'kind', + 'name', + 'visibility', + 'callingconvention', + 'path', + 'methodbinding', + 'reintroduce', + 'overload', + 'abstract', + 'inline', + 'align' + ); + +implementation + +end. diff --git a/References/DelphiAST/Source/DelphiAST.ProjectIndexer.pas b/References/DelphiAST/Source/DelphiAST.ProjectIndexer.pas new file mode 100644 index 000000000..4370f5469 --- /dev/null +++ b/References/DelphiAST/Source/DelphiAST.ProjectIndexer.pas @@ -0,0 +1,615 @@ +unit DelphiAST.ProjectIndexer; + +{$IFDEF FPC}{$MODE DELPHI}{$ENDIF} + +interface + +uses + Classes, Generics.Defaults, Generics.Collections, + SimpleParser.Lexer.Types, + DelphiAST, DelphiAST.Classes, DelphiAST.Consts; + +type + TProjectIndexer = class + strict private type + TParsedUnitsCache = TObjectDictionary<string,TSyntaxNode>; + TUnitPathsCache = TDictionary<string,string>; + + TIncludeInfo = record + FileName: string; + Content : string; + end; + + TIncludeCache = TDictionary<string,TIncludeInfo>; + + public type + TOption = (piUseDefinesDefinedByCompiler); + TOptions = set of TOption; + TGetUnitSyntaxEvent = procedure (Sender: TObject; const fileName: string; + var syntaxTree: TSyntaxNode; var doParseUnit, doAbort: boolean) of object; + TUnitParsedEvent = procedure (Sender: TObject; const unitName: string; const fileName: string; + var syntaxTree: TSyntaxNode; syntaxTreeFromParser: boolean; var doAbort: boolean) of object; + + TUnitInfo = record + Name: string; + Path: string; + SyntaxTree: TSyntaxNode; + HasError: boolean; + ErrorInfo: record + Line: integer; + Col: integer; + Error: string; + end; + end; + + TParsedUnits = class(TList<TUnitInfo>) + protected + procedure Initialize(parsedUnits: TParsedUnitsCache; unitPaths: TUnitPathsCache); + end; + + TIncludeFileInfo = record + Name: string; + Path: string; + end; + + TIncludeFiles = class(TList<TIncludeFileInfo>) + protected + procedure Initialize(includeCache: TIncludeCache); + end; + + TProblemType = (ptCantFindFile, ptCantOpenFile, ptCantParseFile); + TProblemInfo = record + ProblemType: TProblemType; + FileName : string; + Description: string; + end; + + TProblems = class(TList<TProblemInfo>) + protected + procedure LogProblem(problemType: TProblemType; const fileName, description: string); + end; + + strict private type + TIncludeHandler = class(TInterfacedObject, IIncludeHandler) + strict private + [weak] FIncludeCache: TIncludeCache; + [weak] FIndexer : TProjectIndexer; + [weak] FProblems : TProblems; + FUnitFile : string; + FUnitFileFolder : string; + public + constructor Create(indexer: TProjectIndexer; includeCache: TIncludeCache; + problemList: TProblems; const currentFile: string); + function GetIncludeFileContent(const ParentFileName, FileName: string; out Content: string; + out filePath: string): Boolean; + end; + + var + FAborting : boolean; + FDefines : string; + FDefinesList : TStringList; + FIncludeCache : TIncludeCache; + FIncludeFiles : TIncludeFiles; + FNotFoundUnits : TStringList; + FOnGetUnitSyntax: TGetUnitSyntaxEvent; + FOnUnitParsed : TUnitParsedEvent; + FOptions : TOptions; + FParsedUnits : TParsedUnitsCache; + FParsedUnitsInfo: TParsedUnits; + FProblems : TProblems; + FProjectFolder : string; + FSearchPath : string; + FSearchPaths : TStringList; + FUnitPaths : TUnitPathsCache; + strict protected + procedure AppendUnits(usesNode: TSyntaxNode; const filePath: string; unitList: TStrings); + procedure BuildUsesList(unitNode: TSyntaxNode; const fileName: string; isProject: boolean; + unitList: TStringList); + function FindType(node: TSyntaxNode; nodeType: TSyntaxNodeType): TSyntaxNode; + procedure GetUnitSyntax(const fileName: string; var syntaxTree: TSyntaxNode; var + doParseUnit: boolean); + procedure NotifyUnitParsed(const unitName, fileName: string; var syntaxTree: TSyntaxNode; + syntaxTreeFromParser: boolean); + procedure ParseUnit(const unitName: string; const fileName: string; isProject: boolean); + procedure PrepareSearchPath; + procedure PrepareDefines; + procedure RunParserOnUnit(const fileName: string; var syntaxTree: TSyntaxNode); + procedure ScanUsedUnits(const unitName, fileName: string; isProject: boolean; + syntaxTree: TSyntaxNode; syntaxTreeFromParser: boolean); + protected + function FindFile(const fileName: string; relativeToFolder: string; var filePath: string): boolean; + class function SafeOpenFileStream(const fileName: string; var fileStream: TStringStream; + var errorMsg: string): boolean; + public + constructor Create; + destructor Destroy; override; + procedure Index(const fileName: string); + property Defines: string read FDefines write FDefines; + property Options: TOptions read FOptions write FOptions default [piUseDefinesDefinedByCompiler]; + property ParsedUnits: TParsedUnits read FParsedUnitsInfo; + property IncludeFiles: TIncludeFiles read FIncludeFiles; + property Problems: TProblems read FProblems; + property NotFoundUnits: TStringList read FNotFoundUnits; + property SearchPath: string read FSearchPath write FSearchPath; + property OnGetUnitSyntax: TGetUnitSyntaxEvent read FOnGetUnitSyntax write FOnGetUnitSyntax; + property OnUnitParsed: TUnitParsedEvent read FOnUnitParsed write FOnUnitParsed; + end; + +implementation + +uses + SysUtils, + SimpleParser; + +{ TProjectIndexer.TParsedUnits } + +procedure TProjectIndexer.TParsedUnits.Initialize(parsedUnits: TParsedUnitsCache; + unitPaths: TUnitPathsCache); +var + info : TUnitInfo; + kv : TPair<string,TSyntaxNode>; + unitPath: string; +begin + Clear; + Capacity := parsedUnits.Count; + + for kv in parsedUnits do begin + if not assigned(kv.Value) then + continue; //for kv + info.Name := kv.Key; + info.SyntaxTree := kv.Value; + if not (unitPaths.TryGetValue(kv.Key + '.pas', unitPath) + or unitPaths.TryGetValue(kv.Key + '.dpr', unitPath)) + then + unitPath := ''; + info.Path := unitPath; + info.HasError := false; // TODO 1 -oPrimoz Gabrijelcic : fix that + Add(info); + end; + + TrimExcess; + Sort( + TComparer<TUnitInfo>.Construct( + function(const Left, Right: TUnitInfo): integer + begin + Result := TOrdinalIStringComparer(TIStringComparer.Ordinal).Compare(Left.Name, Right.Name); + end)); +end; + +{ TProjectIndexer.TIncludeFiles } + +procedure TProjectIndexer.TIncludeFiles.Initialize(includeCache: TIncludeCache); +var + info: TIncludeFileInfo; + kv : TPair<string,TIncludeInfo>; + p : integer; +begin + Clear; + Capacity := includeCache.Count; + + for kv in includeCache do begin + p := Pos(#13, kv.Key); + if p = 0 then + continue; //for kv + + info.Name := Copy(kv.Key, 1, p-1); + info.Path := kv.Value.FileName; + Add(info); + end; + + TrimExcess; + Sort( + TComparer<TIncludeFileInfo>.Construct( + function(const Left, Right: TIncludeFileInfo): integer + begin + Result := TOrdinalIStringComparer(TIStringComparer.Ordinal).Compare(Left.Name, Right.Name); + end)); +end; + +{ TProjectIndexer.TProblems } + +procedure TProjectIndexer.TProblems.LogProblem(problemType: TProblemType; const fileName, + description: string); +var + info: TProblemInfo; +begin + info.ProblemType := problemType; + info.FileName := fileName; + info.Description := description; + Add(info); +end; + +{ TProjectIndexer } + +procedure TProjectIndexer.AppendUnits(usesNode: TSyntaxNode; const filePath: string; + unitList: TStrings); +var + childNode: TSyntaxNode; + unitName : string; + unitPath : string; +begin + for childNode in usesNode.ChildNodes do + if childNode.Typ = ntUnit then begin + unitName := childNode.GetAttribute(anName); + unitList.Add(unitName); + if not FUnitPaths.ContainsKey(unitName) then begin + unitPath := childNode.GetAttribute(anPath); + if unitPath <> '' then begin + if IsRelativePath(unitPath) then + unitPath := filePath + unitPath; + FUnitPaths.Add(unitName + '.pas', unitPath); + end; + end; + end; +end; + +procedure TProjectIndexer.BuildUsesList(unitNode: TSyntaxNode; const fileName: string; + isProject: boolean; unitList: TStringList); +var + fileFolder: string; + implNode : TSyntaxNode; + intfNode : TSyntaxNode; + usesNode : TSyntaxNode; +begin + fileFolder := IncludeTrailingPathDelimiter(ExtractFilePath(fileName)); + if isProject then begin + usesNode := FindType(unitNode, ntUses); + if assigned(usesNode) then + AppendUnits(usesNode, fileFolder, unitList); + usesNode := FindType(unitNode, ntContains); + if assigned(usesNode) then + AppendUnits(usesNode, fileFolder, unitList); + end + else begin + intfNode := FindType(unitNode, ntInterface); + if assigned(intfNode) then begin + usesNode := FindType(intfNode, ntUses); + if assigned(usesNode) then + AppendUnits(usesNode, fileFolder, unitList); + end; + implNode := FindType(unitNode, ntImplementation); + if assigned(implNode) then begin + usesNode := FindType(implNode, ntUses); + if assigned(usesNode) then + AppendUnits(usesNode, fileFolder, unitList); + end; + end; +end; + +constructor TProjectIndexer.Create; +begin + inherited Create; + FOptions := [piUseDefinesDefinedByCompiler]; + FSearchPaths := TStringList.Create; + FSearchPaths.Delimiter := ';'; + FSearchPaths.StrictDelimiter := true; + FDefinesList := TStringList.Create; + FDefinesList.Delimiter := ';'; + FDefinesList.StrictDelimiter := true; + FParsedUnits := TParsedUnitsCache.Create([doOwnsValues], TIStringComparer.Ordinal); + FParsedUnitsInfo := TParsedUnits.Create; + FIncludeFiles := TIncludeFiles.Create; + FNotFoundUnits := TStringList.Create; + FNotFoundUnits.Sorted := true; + FNotFoundUnits.Duplicates := dupIgnore; + FProblems := TProblems.Create; +end; + +destructor TProjectIndexer.Destroy; +begin + FreeAndNil(FProblems); + FreeAndNil(FNotFoundUnits); + FreeAndNil(FIncludeFiles); + FreeAndNil(FParsedUnitsInfo); + FreeAndNil(FDefinesList); + FreeAndNil(FParsedUnits); + FreeAndNil(FSearchPaths); + inherited; +end; + +function TProjectIndexer.FindFile(const fileName: string; relativeToFolder: string; + var filePath: string): boolean; +var + fName : string; + searchPath: string; + + function FilePresent(const testFile: string): boolean; + begin + Result := FileExists(testFile); + if Result then begin + filePath := ExpandFileName(testFile); + FUnitPaths.Add(fName, filePath); + end; + end; + +begin + Result := true; + fName := fileName.DeQuotedString; + + if FUnitPaths.TryGetValue(fName, filePath) then + Exit; + + if relativeToFolder <> '' then + if FilePresent(relativeToFolder + fName) then + Exit; + + if FilePresent(FProjectFolder + fName) then + Exit; + + for searchPath in FSearchPaths do + if FilePresent(searchPath + fName) then + Exit; + + if SameText(ExtractFileExt(fileName), '.pas') then + Result := false + else + Result := FindFile(fileName + '.pas', relativeToFolder, filePath); + + if (not Result) and (relativeToFolder = '') {ignore include files} then + FNotFoundUnits.Add(fName); +end; + +function TProjectIndexer.FindType(node: TSyntaxNode; nodeType: TSyntaxNodeType): + TSyntaxNode; +begin + if node.Typ = nodeType then + Exit(node) + else + Result := node.FindNode(nodeType); +end; + +procedure TProjectIndexer.GetUnitSyntax(const fileName: string; var syntaxTree: + TSyntaxNode; var doParseUnit: boolean); +var + doAbort: boolean; +begin + doAbort := false; + doParseUnit := true; + syntaxTree := nil; + if assigned(OnGetUnitSyntax) then begin + OnGetUnitSyntax(Self, fileName, syntaxTree, doParseUnit, doAbort); + if doAbort then + FAborting := true; + end; +end; + +procedure TProjectIndexer.Index(const fileName: string); +var + filePath : string; + projectName: string; +begin + FAborting := false; + FParsedUnits.Clear; + FProjectFolder := IncludeTrailingPathDelimiter(ExtractFilePath(fileName)); + FIncludeCache := TIncludeCache.Create; + try + FUnitPaths := TUnitPathsCache.Create(TIStringComparer.Ordinal); + try + PrepareDefines; + PrepareSearchPath; + FNotFoundUnits.Clear; + FProblems.Clear; + filePath := ExpandFileName(fileName); + projectName := ChangeFileExt(ExtractFileName(fileName), ''); + FUnitPaths.Add(projectName + '.dpr', fileName); + ParseUnit(projectName, filePath, true); + FParsedUnitsInfo.Initialize(FParsedUnits, FUnitPaths); + FIncludeFiles.Initialize(FIncludeCache); + finally FreeAndNil(FUnitPaths); end; + finally FreeAndNil(FIncludeCache); end; +end; + +procedure TProjectIndexer.NotifyUnitParsed(const unitName, fileName: string; var + syntaxTree: TSyntaxNode; syntaxTreeFromParser: boolean); +var + doAbort: boolean; +begin + if assigned(OnUnitParsed) then begin + doAbort := false; + OnUnitParsed(Self, unitName, fileName, syntaxTree, syntaxTreeFromParser, doAbort); + if doAbort then + FAborting := true; + end; +end; + +procedure TProjectIndexer.ParseUnit(const unitName: string; const fileName: string; isProject: boolean); +var + doParseUnit: boolean; + syntaxTree : TSyntaxNode; +begin + if FAborting then + Exit; + + GetUnitSyntax(fileName, syntaxTree, doParseUnit); + + if FAborting then + Exit; + + if doParseUnit then + RunParserOnUnit(fileName, syntaxTree); + + FParsedUnits.Add(unitName, syntaxTree); + + if (not FAborting) and assigned(syntaxTree) then + ScanUsedUnits(unitName, fileName, isProject, syntaxTree, doParseUnit); +end; + +procedure TProjectIndexer.PrepareSearchPath; +var + iPath: integer; + sPath: string; +begin + FSearchPaths.DelimitedText := SearchPath; + for iPath := 0 to FSearchPaths.Count - 1 do begin + sPath := FSearchPaths[iPath]; + if IsRelativePath(sPath) then + sPath := FProjectFolder + sPath; + FSearchPaths[iPath] := IncludeTrailingPathDelimiter(sPath); + end; +end; + +class function TProjectIndexer.SafeOpenFileStream(const fileName: string; var fileStream: + TStringStream; var errorMsg: string): boolean; +var + buf : TBytes; + encoding : TEncoding; + readStream: TStream; +begin + Result := true; + try + readStream := TFileStream.Create(fileName, fmOpenRead or fmShareDenyWrite); + except + on E: EFCreateError do begin + errorMsg := E.Message; + Result := false; + end; + on E: EFOpenError do begin + errorMsg := E.Message; + Result := false; + end; + end; + + if Result then try + SetLength(buf, 4); + SetLength(buf, readStream.Read(buf[0], Length(buf))); + encoding := nil; + readStream.Position := TEncoding.GetBufferEncoding(buf, encoding); + fileStream := TStringStream.Create('', encoding); + fileStream.CopyFrom(readStream, readStream.Size - readStream.Position); + finally FreeAndNil(readStream); end; +end; + +procedure TProjectIndexer.PrepareDefines; +begin + FDefinesList.DelimitedText := FDefines; +end; + +procedure TProjectIndexer.RunParserOnUnit(const fileName: string; var syntaxTree: + TSyntaxNode); +var + builder : TPasSyntaxTreeBuilder; + define : string; + errorMsg : string; + fileStream: TStringStream; +begin + if not SafeOpenFileStream(fileName, fileStream, errorMsg) then + FProblems.LogProblem(ptCantOpenFile, fileName, errorMsg) + else try + builder := TPasSyntaxTreeBuilder.Create; + try + builder.IncludeHandler := TIncludeHandler.Create(Self, FIncludeCache, FProblems, fileName); + if piUseDefinesDefinedByCompiler in Options then + builder.InitDefinesDefinedByCompiler; + for define in FDefinesList do + TmwSimplePasPar(builder).Lexer.AddDefine(define); + try + syntaxTree := builder.Run(fileStream); + except + on E: ESyntaxTreeException do begin + FProblems.LogProblem(ptCantParseFile, fileName, + Format('Line %d, Column %d: %s', [E.Line, E.Col, E.Message])); + end; + end; + finally FreeAndNil(builder); end; + finally FreeAndNil(fileStream); end; +end; { TProjectIndexer.RunParserOnUnit } + +procedure TProjectIndexer.ScanUsedUnits(const unitName, fileName: string; isProject: boolean; + syntaxTree: TSyntaxNode; syntaxTreeFromParser: boolean); +var + unitList: TStringList; + unitNode: TSyntaxNode; + usesName: string; + usesPath: string; +begin + unitNode := FindType(syntaxTree, ntUnit); + if not assigned(unitNode) then + Exit; + + unitList := TStringList.Create; + try + BuildUsesList(unitNode, fileName, isProject, unitList); + + NotifyUnitParsed(unitName, fileName, syntaxTree, syntaxTreeFromParser); + + for usesName in unitList do begin + if FAborting then + Exit; + if not FParsedUnits.ContainsKey(usesName) then begin + if FindFile(usesName + '.pas', '', usesPath) then + ParseUnit(usesName, usesPath, false) + else + FParsedUnits.Add(usesName, nil); + end; + end; + finally FreeAndNil(unitList); end; +end; + +{ TProjectIndexer.TIncludeHandler } + +constructor TProjectIndexer.TIncludeHandler.Create(indexer: TProjectIndexer; + includeCache: TIncludeCache; problemList: TProblems; const currentFile: string); +begin + inherited Create; + FIndexer := indexer; + FIncludeCache := includeCache; + FProblems := problemList; + FUnitFileFolder := IncludeTrailingPathDelimiter(ExtractFilePath(currentFile)); + FUnitFile := ChangeFileExt(ExtractFileName(currentFile), ''); +end; + +function TProjectIndexer.TIncludeHandler.GetIncludeFileContent( + const ParentFileName, fileName: string; out Content: string; out filePath: string): Boolean; +var + errorMsg : string; + fileStream : TStringStream; + fName : string; + includeInfo: TIncludeInfo; + key : string; +begin + if fileName.StartsWith('*.') then + fName := FUnitFile + fileName.Remove(0 {0-based}, 1) + else if fileName.Contains('*') then + fName := fileName.Replace('*', '', [rfReplaceAll]) + else + fName := fileName; + + key := fName + #13 + FUnitFileFolder; + if FIncludeCache.TryGetValue(key, includeInfo) then + begin + Content := includeInfo.Content; + filePath := includeInfo.FileName; + Exit(True); + end; + + if not FIndexer.FindFile(fName, FUnitFileFolder, filePath) then begin + FProblems.LogProblem(ptCantFindFile, fName, 'Source folder: ' + FUnitFileFolder); + includeInfo.FileName := ''; + includeInfo.Content := ''; + FIncludeCache.Add(key, includeInfo); + Exit(False); + end; + + if FIncludeCache.TryGetValue(filePath, includeInfo) then + begin + Content := includeInfo.Content; + Exit(True); + end; + + if not TProjectIndexer.SafeOpenFileStream(filePath, fileStream, errorMsg) then begin + FProblems.LogProblem(ptCantOpenFile, filePath, errorMsg); + Result := False; + end + else try + Content := fileStream.DataString; + Result := True; + finally FreeAndNil(fileStream); end; + + includeInfo.FileName := filePath; + includeInfo.Content := Content; + FIncludeCache.Add(fName + #13 + FUnitFileFolder, includeInfo); + includeInfo.FileName := ''; + FIncludeCache.Add(filePath, includeInfo); +end; + +end. diff --git a/References/DelphiAST/Source/DelphiAST.Serialize.Binary.pas b/References/DelphiAST/Source/DelphiAST.Serialize.Binary.pas new file mode 100644 index 000000000..04ac28342 --- /dev/null +++ b/References/DelphiAST/Source/DelphiAST.Serialize.Binary.pas @@ -0,0 +1,335 @@ + unit DelphiAST.Serialize.Binary; + + {$IFDEF FPC}{$MODE DELPHI}{$ENDIF} + +interface + +uses + Classes, + Generics.Collections, + DelphiAST.Consts, + DelphiAST.Classes; + +type + TNodeClass = (ntSyntax, ntCompound, ntValued, ntComment); + + TBinarySerializer = class + strict private + FStream : TStream; + FStringList : TStringList; + FStringTable: TDictionary<string,integer>; + strict protected + function CheckSignature: boolean; + function CheckVersion: boolean; + function CreateNode(nodeClass: TNodeClass; nodeType: TSyntaxNodeType): TSyntaxNode; + function ReadNode(var node: TSyntaxNode): boolean; + function ReadNumber(var num: cardinal): Boolean; + function ReadString(var str: string): boolean; + function WriteNode(Node: TSyntaxNode): Boolean; + function WriteNumber(Num: cardinal): Boolean; + function WriteString(const S: string): Boolean; + public + function Read(Stream: TStream; var Root: TSyntaxNode): boolean; + function Write(Stream: TStream; Root: TSyntaxNode): boolean; + end; + +implementation + +uses + SysUtils; + +var + CSignature: AnsiString = 'DAST binary file'#26; + +function TBinarySerializer.CheckSignature: boolean; +var + sig: AnsiString; +begin + SetLength(sig, Length(CSignature)); + Result := (FStream.Read(sig[1], Length(CSignature)) = Length(CSignature)) + and (sig = CSignature); +end; + +function TBinarySerializer.CheckVersion: boolean; +var + version: Integer; +begin + Result := (FStream.Read(version, 4) = 4) + and ((version AND $FFFF0000) = $01000000); +end; + +function TBinarySerializer.CreateNode(nodeClass: TNodeClass; nodeType: TSyntaxNodeType): + TSyntaxNode; +begin + case nodeClass of + ntSyntax: Result := TSyntaxNode.Create(nodeType); + ntCompound: Result := TCompoundSyntaxNode.Create(nodeType); + ntValued: Result := TValuedSyntaxNode.Create(nodeType); + ntComment: Result := TCommentNode.Create(nodeType); + else raise Exception.Create('TBinarySerializer.CreateNode: Unexpected node class'); + end; +end; + +function TBinarySerializer.Read(Stream: TStream; var Root: TSyntaxNode): boolean; +var + node: TSyntaxNode; +begin + Result := false; + + FStringList := TStringList.Create; + try + FStream := Stream; + if not CheckSignature then + Exit; + if not CheckVersion then + Exit; + if not ReadNode(node) then + Exit; + Root := node; + finally FStringList.Free; end; + + Result := true; +end; + +function TBinarySerializer.ReadNode(var node: TSyntaxNode): boolean; +var + childNode: TSyntaxNode; + i : Integer; + nodeClass: TNodeClass; + num : cardinal; + numSub: cardinal; + str : string; +begin + Result := false; + node := nil; + if (not ReadNumber(num)) or (num > cardinal(Ord(High(TNodeClass)))) then + Exit; + nodeClass := TNodeClass(num); + if (not ReadNumber(num)) or (num > Ord(High(TSyntaxNodeType))) then + Exit; + node := CreateNode(nodeClass, TSyntaxNodeType(num)); + try + + if (not ReadNumber(num)) or (num > cardinal(High(integer))) then + Exit; + Node.Col := num; + if (not ReadNumber(num)) or (num > cardinal(High(integer))) then + Exit; + Node.Line := num; + + case nodeClass of + ntCompound: + begin + if (not ReadNumber(num)) or (num > cardinal(High(integer))) then + Exit; + TCompoundSyntaxNode(Node).EndCol := num; + if (not ReadNumber(num)) or (num > cardinal(High(integer))) then + Exit; + TCompoundSyntaxNode(Node).EndLine := num; + end; + ntValued: + begin + if not ReadString(str) then + Exit; + TValuedSyntaxNode(Node).Value := str; + end; + ntComment: + begin + if not ReadString(str) then + Exit; + TCommentNode(Node).Text := str; + end; + end; + + if not ReadNumber(numSub) then + Exit; + for i := 1 to numSub do begin + if (not ReadNumber(num)) or (num > cardinal(Ord(High(TAttributeName)))) then + Exit; + if not ReadString(str) then + Exit; + Node.SetAttribute(TAttributeName(num), str); + end; + + if not ReadNumber(numSub) then + Exit; + for i := 1 to numSub do begin + if not ReadNode(childNode) then + Exit; + Node.AddChild(childNode); + end; + + Result := true; + finally + if not Result then begin + node.Free; + node := nil; + end; + end; +end; + +function TBinarySerializer.ReadNumber(var num: cardinal): Boolean; +var + lowPart: byte; + shift : Integer; +begin + Result := false; + + shift := 0; + num := 0; + repeat + if FStream.Read(lowPart, 1) <> 1 then + Exit; + num := num OR ((lowPart AND $7F) SHL shift); + Inc(shift, 7); + until (lowPart AND $80) = 0; + + Result := true; +end; + +function TBinarySerializer.ReadString(var str: string): boolean; +var + id: integer; + len: cardinal; + u8: UTF8String; +begin + Result := false; + + if not ReadNumber(len) then + Exit; + if (len SHR 24) = $FF then begin + id := len AND $00FFFFFF; + if id >= FStringList.Count then + Exit; + str := FStringList[id]; + end + else begin + SetLength(u8, len); + if len > 0 then + if cardinal(FStream.Read(u8[1], len)) <> len then + Exit; + str := UTF8ToUnicodeString(u8); + if Length(Str) > 4 then + FStringList.Add(str); + end; + + Result := true; +end; + +function TBinarySerializer.Write(Stream: TStream; Root: TSyntaxNode): boolean; +var + version: Integer; +begin + Result := false; + + FStringTable := TDictionary<string,integer>.Create; + try + FStream := Stream; + if FStream.Write(CSignature[1], Length(CSignature)) <> Length(CSignature) then + Exit; + version := $01000000; + if FStream.Write(version, 4) <> 4 then + Exit; + if not WriteNode(Root) then + Exit; + finally FStringTable.Free; end; + + Result := true; +end; + +function TBinarySerializer.WriteNode(Node: TSyntaxNode): Boolean; +var + attr : TAttributeEntry; + childNode: TSyntaxNode; + nodeClass: TNodeClass; +begin + Result := false; + + if Node is TCompoundSyntaxNode then + nodeClass := ntCompound + else if Node is TValuedSyntaxNode then + nodeClass := ntValued + else if Node is TCommentNode then + nodeClass := ntComment + else + nodeClass := ntSyntax; + + if not WriteNumber(Ord(nodeClass)) then Exit; + if not WriteNumber(Ord(Node.Typ)) then Exit; + if not WriteNumber(Node.Col) then Exit; + if not WriteNumber(Node.Line) then Exit; + + case nodeClass of + ntCompound: + begin + if not WriteNumber(TCompoundSyntaxNode(Node).EndCol) then Exit; + if not WriteNumber(TCompoundSyntaxNode(Node).EndLine) then Exit; + end; + ntValued: + if not WriteString(TValuedSyntaxNode(Node).Value) then Exit; + ntComment: + if not WriteString(TCommentNode(Node).Text) then Exit; + end; + + if not WriteNumber(Length(Node.Attributes)) then Exit; + for attr in Node.Attributes do begin // causes dynamic array assignment, yuck + if not WriteNumber(Ord(attr.Key)) then Exit; + if not WriteString(attr.Value) then Exit; + end; + + if not WriteNumber(Length(Node.ChildNodes)) then Exit; + for childNode in Node.ChildNodes do // causes dynamic array assignment, yuck + if not WriteNode(childNode) then Exit; + + Result := true; +end; + +function TBinarySerializer.WriteNumber(Num: cardinal): Boolean; +var + lowPart: byte; +begin + Result := false; + + repeat + lowPart := Num AND $7F; + Num := Num SHR 7; + if Num <> 0 then + lowPart := lowPart OR $80; + if FStream.Write(lowPart, 1) <> 1 then + Exit; + until Num = 0; + + Result := true; +end; + +function TBinarySerializer.WriteString(const S: string): Boolean; +var + i: Integer; + id: integer; + u8: UTF8String; +begin + Result := false; + + if (Length(S) > 4) and FStringTable.TryGetValue(S, id) then begin + if not WriteNumber(cardinal(id) OR $FF000000) then + Exit; + end + else begin + if Length(S) > 4 then begin + FStringTable.Add(S, FStringTable.Count); + if FStringTable.Count > $FFFFFF then + raise Exception.Create('TBinarySerializer.WriteString: Too many strings!'); + end; + u8 := UTF8Encode(s); + i := Length(u8); + if not WriteNumber(i) then + Exit; + if i > 0 then + if FStream.Write(u8[1], i) <> i then + Exit; + end; + + Result := true; +end; + +end. diff --git a/References/DelphiAST/Source/DelphiAST.SimpleParserEx.pas b/References/DelphiAST/Source/DelphiAST.SimpleParserEx.pas new file mode 100644 index 000000000..6afd71050 --- /dev/null +++ b/References/DelphiAST/Source/DelphiAST.SimpleParserEx.pas @@ -0,0 +1,422 @@ +unit DelphiAST.SimpleParserEx; + +interface + +uses + SysUtils, Generics.Collections, SimpleParser, SimpleParser.Lexer.Types, + SimpleParser.Lexer, Classes; + +type + TStringEvent = procedure(var s: string) of object; + + TPasLexer = class + private + FLexer: TmwPasLex; + FOnHandleString: TStringEvent; + function GetToken: string; inline; + function GetPosXY: TTokenPoint; inline; + function GetFileName: string; + public + constructor Create(const ALexer: TmwPasLex; AOnHandleString: TStringEvent); + property FileName: string read GetFileName; + property PosXY: TTokenPoint read GetPosXY; + property Token: string read GetToken; + property Lexer: TmwPasLex read FLexer; + end; + + TmwSimplePasParEx = class(TmwSimplePasPar) + public type + TNameListStack = class; + TNameList = class + public type + TNameItem = class + public type + TNameItemToken = class + strict private + FTokenFileName: string; + FTokenPoint: TTokenPoint; + FTokenPos: Integer; + FTokenLen: Integer; + public + constructor Create(const ATokenFileName: string; + const ATokenPoint: TTokenPoint; const ATokenPos, ATokenLen: Integer); + property TokenFileName: string read FTokenFileName; + property TokenPoint: TTokenPoint read FTokenPoint; + property TokenPos: Integer read FTokenPos; + property TokenLen: Integer read FTokenLen; + end; + strict private + FTokenList: TObjectList<TNameItemToken>; + FEndNameCalled: Boolean; + function GetLastNameItemToken: TNameItemToken; + public + constructor Create(const ATokenFileName: string; + const ATokenPoint: TTokenPoint; const ATokenID: TptTokenKind; + const ATokenPos, ATokenLen: Integer); + destructor Destroy; override; + procedure AddToken(const ATokenFileName: string; + const ATokenPoint: TTokenPoint; const ATokenID: TptTokenKind; + const ATokenPos, ATokenLen: Integer); + property TokenList: TObjectList<TNameItemToken> read FTokenList; + property LastNameItemToken: TNameItemToken read GetLastNameItemToken; + property EndNameCalled: Boolean read FEndNameCalled write FEndNameCalled; + end; + strict private + FParser: TmwSimplePasParEx; + FNameItems: TObjectList<TNameItem>; + FAutoCreated: Boolean; + function GetItems(const Index: Integer): TNameItem; inline; + function GetLastItem: TNameItem; inline; + function GetOriginalNames(const Index: Integer): string; + function GetNames(const Index: Integer): string; + function GetLastOriginalName: string; + function GetLastName: string; + function GetCount: Integer; inline; + function GetLexer: TmwPasLex; inline; + property Lexer: TmwPasLex read GetLexer; + public + constructor Create(const AParser: TmwSimplePasParEx; + const AAutoCreated: Boolean); + destructor Destroy; override; + procedure BeginName; + procedure EndName; + procedure AddToken; + property AutoCreated: Boolean read FAutoCreated; + property Items[const Index: Integer]: TNameItem read GetItems; + property LastItem: TNameItem read GetLastItem; + property OriginalNames[const Index: Integer]: string read GetOriginalNames; + property Names[const Index: Integer]: string read GetNames; default; + property LastOriginalName: string read GetLastOriginalName; + property LastName: string read GetLastName; + property Count: Integer read GetCount; + end; + TNameListStack = class + strict private + FParser: TmwSimplePasParEx; + FNameListStack: TObjectStack<TNameList>; + public + constructor Create(const AParser: TmwSimplePasParEx); + destructor Destroy; override; + procedure PushNames(const AAutoCreated: Boolean); inline; + procedure PopNames; inline; + function ExtractNames: TNameList; inline; + function PeekNames: TNameList; inline; + function ToArray: TArray<TNameList>; inline; + function Count: Integer; inline; + end; + strict private + FNameListStack: TNameListStack; + FPreviousNames: TNameList; + FLexer: TPasLexer; + FLowerCaseNames: Boolean; + FOnHandleString: TStringEvent; + function GetCurrentNames: TNameList; inline; + strict protected + procedure DoHandleString(var AString: string); inline; + procedure PushNames; inline; + procedure PopNames; inline; + function PeekNames: TNameList; inline; + procedure BeginName; + procedure EndName; + property CurrentNames: TNameList read GetCurrentNames; + property PreviousNames: TNameList read FPreviousNames; + protected + procedure NextToken; override; + public + constructor Create; override; + destructor Destroy; override; + property Lexer: TPasLexer read FLexer; + property LowerCaseNames: Boolean read FLowerCaseNames write FLowerCaseNames; + property OnHandleString: TStringEvent read FOnHandleString write FOnHandleString; + end; + +implementation + +{ TPasLexer } + +constructor TPasLexer.Create(const ALexer: TmwPasLex; AOnHandleString: TStringEvent); +begin + inherited Create; + FLexer := ALexer; + FOnHandleString := AOnHandleString; +end; + +function TPasLexer.GetFileName: string; +begin + Result := FLexer.Buffer.FileName; +end; + +function TPasLexer.GetPosXY: TTokenPoint; +begin + Result := FLexer.PosXY; +end; + +function TPasLexer.GetToken: string; +begin + Result := FLexer.Token; + FOnHandleString(Result); +end; + +{ TmwSimplePasParEx.TNameList.TNameItem.TNameItemToken } + +constructor TmwSimplePasParEx.TNameList.TNameItem.TNameItemToken.Create( + const ATokenFileName: string; const ATokenPoint: TTokenPoint; + const ATokenPos, ATokenLen: Integer); +begin + FTokenFileName := ATokenFileName; + FTokenPoint := ATokenPoint; + FTokenPos := ATokenPos; + FTokenLen := ATokenLen; +end; + +{ TPasNamesBuilder.TNamesList.TNameItem } + +constructor TmwSimplePasParEx.TNameList.TNameItem.Create( + const ATokenFileName: string; const ATokenPoint: TTokenPoint; + const ATokenID: TptTokenKind; const ATokenPos, ATokenLen: Integer); +begin + FTokenList := TObjectList<TNameItemToken>.Create(True); + AddToken(ATokenFileName, ATokenPoint, ATokenID, ATokenPos, ATokenLen); +end; + +destructor TmwSimplePasParEx.TNameList.TNameItem.Destroy; +begin + FTokenList.Free; + inherited; +end; + +procedure TmwSimplePasParEx.TNameList.TNameItem.AddToken( + const ATokenFileName: string; const ATokenPoint: TTokenPoint; + const ATokenID: TptTokenKind; const ATokenPos, ATokenLen: Integer); +begin + if not IsTokenIDJunk(ATokenID) and + ((FTokenList.Count = 0) or (FTokenList.Last.TokenPos < ATokenPos)) then + FTokenList.Add(TNameItemToken.Create( + ATokenFileName, ATokenPoint, ATokenPos, ATokenLen)); +end; + +function TmwSimplePasParEx.TNameList.TNameItem.GetLastNameItemToken: TNameItemToken; +begin + Result := TokenList.Last; +end; + +{ TPasNamesBuilder.TNamesList } + +constructor TmwSimplePasParEx.TNameList.Create(const AParser: TmwSimplePasParEx; + const AAutoCreated: Boolean); +begin + FParser := AParser; + FNameItems := TObjectList<TNameItem>.Create(True); +end; + +destructor TmwSimplePasParEx.TNameList.Destroy; +begin + FNameItems.Free; + inherited; +end; + +function TmwSimplePasParEx.TNameList.GetLexer: TmwPasLex; +begin + Result := FParser.Lexer.Lexer; +end; + +procedure TmwSimplePasParEx.TNameList.BeginName; +begin + FNameItems.Add(TNameItem.Create(Lexer.FileName, Lexer.PosXY, Lexer.TokenID, + Lexer.TokenPos, Lexer.TokenLen)); +end; + +procedure TmwSimplePasParEx.TNameList.EndName; +begin + FNameItems.Last.EndNameCalled := True; +end; + +procedure TmwSimplePasParEx.TNameList.AddToken; +begin + FNameItems.Last.AddToken(Lexer.FileName, Lexer.PosXY, Lexer.TokenID, + Lexer.TokenPos, Lexer.TokenLen); +end; + +function TmwSimplePasParEx.TNameList.GetItems(const Index: Integer): TNameItem; +begin + Result := FNameItems[Index]; +end; + +function TmwSimplePasParEx.TNameList.GetLastItem: TNameItem; +begin + Result := FNameItems.Last; +end; + +function TmwSimplePasParEx.TNameList.GetOriginalNames(const Index: Integer): string; +var + I: Integer; + NameItem: TNameItem; + Token: string; +begin + Result := ''; + NameItem := Items[Index]; + for I := 0 to NameItem.TokenList.Count - 1 do + begin + SetString(Token, Lexer.Buffer.Buf + NameItem.TokenList[I].TokenPos, + NameItem.TokenList[I].TokenLen); + Result := Result + Token; + end; + FParser.DoHandleString(Result); +end; + +function TmwSimplePasParEx.TNameList.GetNames(const Index: Integer): string; +begin + Result := OriginalNames[Index]; + if FParser.LowerCaseNames then + begin + Result := AnsiLowerCase(Result); + FParser.DoHandleString(Result); + end; +end; + +function TmwSimplePasParEx.TNameList.GetLastOriginalName: string; +begin + Result := OriginalNames[Count - 1]; +end; + +function TmwSimplePasParEx.TNameList.GetLastName: string; +begin + Result := Names[Count - 1]; +end; + +function TmwSimplePasParEx.TNameList.GetCount: Integer; +begin + Result := FNameItems.Count; +end; + +{ TPasNamesBuilder.TNameListStack } + +function TmwSimplePasParEx.TNameListStack.Count: Integer; +begin + Result := FNameListStack.Count; +end; + +constructor TmwSimplePasParEx.TNameListStack.Create( + const AParser: TmwSimplePasParEx); +begin + FParser := AParser; + FNameListStack := TObjectStack<TNameList>.Create(True); +end; + +destructor TmwSimplePasParEx.TNameListStack.Destroy; +begin + FNameListStack.Free; + inherited; +end; + +procedure TmwSimplePasParEx.TNameListStack.PushNames(const AAutoCreated: Boolean); +begin + FNameListStack.Push(TNameList.Create(FParser, AAutoCreated)); +end; + +procedure TmwSimplePasParEx.TNameListStack.PopNames; +begin + FNameListStack.Pop; +end; + +function TmwSimplePasParEx.TNameListStack.ExtractNames: TNameList; +begin + Result := FNameListStack.Extract; +end; + +function TmwSimplePasParEx.TNameListStack.PeekNames: TNameList; +begin + Result := FNameListStack.Peek; +end; + +function TmwSimplePasParEx.TNameListStack.ToArray: TArray<TNameList>; +begin + Result := FNameListStack.ToArray; +end; + +{ TmwSimplePasParEx } + +constructor TmwSimplePasParEx.Create; +begin + inherited; + FNameListStack := TNameListStack.Create(Self); + FNameListStack.PushNames(True); + FPreviousNames := TNameList.Create(Self, True); + FLexer := TPasLexer.Create(inherited Lexer, DoHandleString); + FLowerCaseNames := True; +end; + +destructor TmwSimplePasParEx.Destroy; +begin + FLexer.Free; + FPreviousNames.Free; + FNameListStack.PopNames; + FNameListStack.Free; + inherited; +end; + +procedure TmwSimplePasParEx.NextToken; +var + NameList: TNameList; +begin + if FNameListStack.Count > 0 then + for NameList in FNameListStack.ToArray do + if (NameList.Count > 0) and not NameList.LastItem.EndNameCalled then + NameList.AddToken; + inherited; +end; + +procedure TmwSimplePasParEx.PushNames; +begin + FNameListStack.PushNames(False); +end; + +procedure TmwSimplePasParEx.PopNames; +begin + FPreviousNames.Free; + FPreviousNames := FNameListStack.ExtractNames; +end; + +function TmwSimplePasParEx.PeekNames: TNameList; +begin + Result := FNameListStack.PeekNames; +end; + +procedure TmwSimplePasParEx.BeginName; +var + NameList: TNameList; +begin + NameList := FNameListStack.PeekNames; + if (NameList.Count > 0) and not NameList.LastItem.EndNameCalled then + begin + FNameListStack.PushNames(True); + NameList := FNameListStack.PeekNames; + end; + NameList.BeginName; +end; + +procedure TmwSimplePasParEx.EndName; +var + NameList: TNameList; +begin + NameList := FNameListStack.PeekNames; + if NameList.LastItem.EndNameCalled then + begin + FNameListStack.PopNames; + NameList := FNameListStack.PeekNames; + end; + NameList.EndName; +end; + +procedure TmwSimplePasParEx.DoHandleString(var AString: string); +begin + if Assigned(FOnHandleString) then + FOnHandleString(AString); +end; + +function TmwSimplePasParEx.GetCurrentNames: TNameList; +begin + Result := PeekNames; +end; + +end. diff --git a/References/DelphiAST/Source/DelphiAST.Writer.pas b/References/DelphiAST/Source/DelphiAST.Writer.pas new file mode 100644 index 000000000..7e9f05ca0 --- /dev/null +++ b/References/DelphiAST/Source/DelphiAST.Writer.pas @@ -0,0 +1,158 @@ +unit DelphiAST.Writer; + +interface + +uses + {$IFDEF FPC} + StringBuilderUnit, + {$ENDIF} + Classes, + DelphiAST.Classes, SysUtils; + +type + TSyntaxTreeWriter = class + private + class procedure NodeToXML(const Builder: TStringBuilder; + const Node: TSyntaxNode; Formatted: Boolean); static; + public + class function ToXML(const Root: TSyntaxNode; + Formatted: Boolean = False): string; static; + + {$IFNDEF FPC} + class function ToBinary(const Root: TSyntaxNode; Stream: TStream): Boolean; static; + {$ENDIF} + end; + +implementation + +uses + Generics.Collections, + {$IFNDEF FPC} + DelphiAST.Serialize.Binary, + {$ENDIF} + DelphiAST.Consts; + +{$I SimpleParser.inc} +{$IFDEF D18_NEWER} + {$ZEROBASEDSTRINGS OFF} +{$ENDIF} + +{ TSyntaxTreeWriter } + +class procedure TSyntaxTreeWriter.NodeToXML(const Builder: TStringBuilder; + const Node: TSyntaxNode; Formatted: Boolean); + + function XMLEncode(const Data: string): string; + var + i, n: Integer; + + procedure Encode(const s: string); + begin + Move(s[1], Result[n], Length(s) * SizeOf(Char)); + Inc(n, Length(s)); + end; + + begin + SetLength(Result, Length(Data) * 6); + n := 1; + for i := 1 to Length(Data) do + case Data[i] of + '<': Encode('<'); + '>': Encode('>'); + '&': Encode('&'); + '"': Encode('"'); + '''': Encode('''); + else + Result[n] := Data[i]; + Inc(n); + end; + SetLength(Result, n - 1); + end; + + procedure NodeToXMLInternal(const Node: TSyntaxNode; const Indent: string); + var + HasChildren: Boolean; + NewIndent: string; + Attr: TPair<TAttributeName, string>; + ChildNode: TSyntaxNode; + begin + HasChildren := Node.HasChildren; + if Formatted then + begin + NewIndent := Indent + ' '; + Builder.Append(Indent); + end; + Builder.Append('<' + UpperCase(SyntaxNodeNames[Node.Typ])); + + Builder.Append(' line_seq="' + IntToStr(Node.LineSeq) + '"'); + + if Node is TCompoundSyntaxNode then + begin + Builder.Append(' begin_line="' + IntToStr(TCompoundSyntaxNode(Node).Line) + '"'); + Builder.Append(' begin_col="' + IntToStr(TCompoundSyntaxNode(Node).Col) + '"'); + Builder.Append(' end_line="' + IntToStr(TCompoundSyntaxNode(Node).EndLine) + '"'); + Builder.Append(' end_col="' + IntToStr(TCompoundSyntaxNode(Node).EndCol) + '"'); + end else + begin + Builder.Append(' line="' + IntToStr(Node.Line) + '"'); + Builder.Append(' col="' + IntToStr(Node.Col) + '"'); + end; + + if Node.FileName <> '' then + Builder.Append(' file="' + XMLEncode(Node.FileName) + '"'); + + if Node is TValuedSyntaxNode then + Builder.Append(' value="' + XMLEncode(TValuedSyntaxNode(Node).Value) + '"'); + + for Attr in Node.Attributes do + Builder.Append(' ' + AttributeNameStrings[Attr.Key] + '="' + XMLEncode(Attr.Value) + '"'); + if HasChildren then + Builder.Append('>') + else + Builder.Append('/>'); + if Formatted then + Builder.AppendLine; + for ChildNode in Node.ChildNodes do + NodeToXMLInternal(ChildNode, NewIndent); + if HasChildren then + begin + if Formatted then + Builder.Append(Indent); + Builder.Append('</' + UpperCase(SyntaxNodeNames[Node.Typ]) + '>'); + if Formatted then + Builder.AppendLine; + end; + end; + +begin + NodeToXMLInternal(Node, ''); +end; + +{$IFNDEF FPC} +class function TSyntaxTreeWriter.ToBinary(const Root: TSyntaxNode; Stream: TStream): + Boolean; +var + Writer: TBinarySerializer; +begin + Writer := TBinarySerializer.Create; + try + Result := Writer.Write(Stream, Root); + finally FreeAndNil(Writer); end; +end; +{$ENDIF} + +class function TSyntaxTreeWriter.ToXML(const Root: TSyntaxNode; + Formatted: Boolean): string; +var + Builder: TStringBuilder; +begin + Builder := TStringBuilder.Create; + try + NodeToXml(Builder, Root, Formatted); + Result := '<?xml version="1.0"?>' + sLineBreak + Builder.ToString; + finally + Builder.Free; + end; +end; + +end. diff --git a/References/DelphiAST/Source/DelphiAST.pas b/References/DelphiAST/Source/DelphiAST.pas new file mode 100644 index 000000000..c908cc5ce --- /dev/null +++ b/References/DelphiAST/Source/DelphiAST.pas @@ -0,0 +1,2828 @@ +unit DelphiAST; + +{$IFDEF FPC}{$MODE DELPHI}{$ENDIF} + +interface + +uses + SysUtils, Classes, Generics.Collections, SimpleParser, SimpleParser.Lexer, + SimpleParser.Lexer.Types, DelphiAST.Classes, DelphiAST.Consts, DelphiAST.SimpleParserEx; + +type + ESyntaxTreeException = class(EParserException) + strict private + FSyntaxTree: TSyntaxNode; + public + constructor Create(Line, Col: Integer; const FileName, Msg: string; SyntaxTree: TSyntaxNode); reintroduce; + destructor Destroy; override; + + property SyntaxTree: TSyntaxNode read FSyntaxTree write FSyntaxTree; + end; + + TNodeStack = class + strict private + FLexer: TPasLexer; + FStack: TStack<TSyntaxNode>; + + function GetCount: Integer; + public + constructor Create(Lexer: TPasLexer); + destructor Destroy; override; + + function AddChild(Typ: TSyntaxNodeType): TSyntaxNode; overload; + function AddChild(Node: TSyntaxNode): TSyntaxNode; overload; + function AddValuedChild(Typ: TSyntaxNodeType; const Value: string): TSyntaxNode; + + procedure Clear; + function Peek: TSyntaxNode; + function Pop: TSyntaxNode; + + function Push(Typ: TSyntaxNodeType): TSyntaxNode; overload; + function Push(Node: TSyntaxNode): TSyntaxNode; overload; + function PushCompoundSyntaxNode(Typ: TSyntaxNodeType): TSyntaxNode; + function PushValuedNode(Typ: TSyntaxNodeType; const Value: string): TSyntaxNode; + + property Count: Integer read GetCount; + end; + + TPasSyntaxTreeBuilder = class(TmwSimplePasParEx) + private type + TTreeBuilderMethod = procedure of object; + private + procedure BuildExpressionTree(ExpressionMethod: TTreeBuilderMethod); + procedure BuildParametersList(ParametersListMethod: TTreeBuilderMethod); + procedure RearrangeVarSection(const VarSect: TSyntaxNode); + procedure ParserMessage(Sender: TObject; const Typ: TMessageEventType; const Msg: string; X, Y: Integer); + function NodeListToString(NamesNode: TSyntaxNode): string; + procedure MoveMembersToVisibilityNodes(TypeNode: TSyntaxNode); + procedure CallInheritedConstantExpression; + procedure CallInheritedExpression; + procedure CallInheritedFormalParameterList; + procedure CallInheritedPropertyParameterList; + procedure SetCurrentCompoundNodesEndPosition; + procedure DoOnComment(Sender: TObject; const Text: string); + function DequoteString(const S: string): string; + protected + FStack: TNodeStack; + FComments: TObjectList<TCommentNode>; + procedure AccessSpecifier; override; + procedure AdditiveOperator; override; + procedure AddressOp; override; + procedure AlignmentParameter; override; + procedure AnonymousMethod; override; + procedure ArrayBounds; override; + procedure ArrayConstant; override; + procedure ArrayDimension; override; + procedure AsmStatement; override; + procedure AsOp; override; + procedure AssignOp; override; + procedure AtExpression; override; + procedure CaseElseStatement; override; + procedure CaseLabel; override; + procedure CaseLabelList; override; + procedure CaseSelector; override; + procedure CaseStatement; override; + procedure ClassClass; override; + procedure ClassConstraint; override; + procedure ClassField; override; + procedure ClassForward; override; + procedure ClassFunctionHeading; override; + procedure ClassHelper; override; + procedure ClassMethod; override; + procedure ClassMethodResolution; override; + procedure ClassMethodHeading; override; + procedure ClassProcedureHeading; override; + procedure ClassProperty; override; + procedure ClassReferenceType; override; + procedure ClassType; override; + procedure CompoundStatement; override; + procedure ConstParameter; override; + procedure ConstantDeclaration; override; + procedure ConstantExpression; override; + procedure ConstantName; override; + procedure ConstraintList; override; + procedure ConstSection; override; + procedure ConstantValue; override; + procedure ConstantValueTyped; override; + procedure ConstructorConstraint; override; + procedure ConstructorName; override; + procedure ContainsClause; override; + procedure DestructorName; override; + procedure DirectiveBinding; override; + procedure DirectiveBindingMessage; override; + procedure DirectiveCalling; override; + procedure DirectiveInline; override; + procedure DispInterfaceForward; override; + procedure DotOp; override; + procedure ElseExpression; override; + procedure ElseStatement; override; + procedure EmptyStatement; override; + procedure EnumeratedType; override; + procedure ExceptBlock; override; + procedure ExceptionBlockElseBranch; override; + procedure ExceptionHandler; override; + procedure ExceptionVariable; override; + procedure ExportedHeading; override; + procedure ExportsClause; override; + procedure ExportsElement; override; + procedure ExportsName; override; + procedure ExportsNameId; override; + procedure Expression; override; + procedure ExpressionList; override; + procedure ExternalDirective; override; + procedure FieldName; override; + procedure FinalizationSection; override; + procedure FinallyBlock; override; + procedure FormalParameterList; override; + procedure ForStatement; override; + procedure ForStatementDownTo; override; + procedure ForStatementFrom; override; + procedure ForStatementIn; override; + procedure ForStatementTo; override; + procedure FunctionHeading; override; + procedure FunctionMethodName; override; + procedure FunctionProcedureName; override; + procedure GotoStatement; override; + procedure TernaryOp; override; + procedure IfStatement; override; + procedure Identifier; override; + procedure ImplementationSection; override; + procedure ImplementsSpecifier; override; + procedure IndexSpecifier; override; + procedure IndexOp; override; + procedure InheritedStatement; override; + procedure InheritedVariableReference; override; + procedure InitializationSection; override; + procedure InlineVarDeclaration; override; + procedure InlineVarSection; override; + procedure InterfaceForward; override; + procedure InterfaceGUID; override; + procedure InterfaceSection; override; + procedure InterfaceType; override; + procedure IsNotOp; override; + procedure LabelId; override; + procedure MainUsesClause; override; + procedure MainUsedUnitStatement; override; + procedure MethodKind; override; + procedure MultiplicativeOperator; override; + procedure NotInOp; override; + procedure NotOp; override; + procedure NilToken; override; + procedure Number; override; + procedure ObjectNameOfMethod; override; + procedure OutParameter; override; + procedure ParameterFormal; override; + procedure ParameterName; override; + procedure PointerSymbol; override; + procedure PointerType; override; + procedure ProceduralType; override; + procedure ProcedureHeading; override; + procedure ProcedureDeclarationSection; override; + procedure ProcedureProcedureName; override; + procedure PropertyName; override; + procedure PropertyParameterList; override; + procedure RaiseStatement; override; + procedure RecordAlignValue; override; + procedure RecordConstraint; override; + procedure RecordFieldConstant; override; + procedure RecordType; override; + procedure RelativeOperator; override; + procedure RepeatStatement; override; + procedure ResourceDeclaration; override; + procedure ResourceValue; override; + procedure RequiresClause; override; + procedure RequiresIdentifier; override; + procedure RequiresIdentifierId; override; + procedure ReturnType; override; + procedure RoundClose; override; + procedure RoundOpen; override; + procedure SetConstructor; override; + procedure SetElement; override; + procedure SimpleStatement; override; + procedure SimpleType; override; + procedure StatementList; override; + procedure StorageDefault; override; + procedure StringConst; override; + procedure StringConstSimple; override; + procedure StringStatement; override; + procedure StructuredType; override; + procedure SubrangeType; override; + procedure ThenExpression; override; + procedure ThenStatement; override; + procedure TryStatement; override; + procedure TypeArgs; override; + procedure TypeDeclaration; override; + procedure TypeId; override; + procedure TypeParamDecl; override; + procedure TypeParams; override; + procedure TypeSection; override; + procedure TypeSimple; override; + procedure UnaryMinus; override; + procedure UnitFile; override; + procedure UnitName; override; + procedure UnitId; override; + procedure UsesClause; override; + procedure UsedUnitName; override; + procedure VarAbsolute; override; + procedure VarDeclaration; override; + procedure VarName; override; + procedure VarParameter; override; + procedure VarSection; override; + procedure VisibilityPrivate; override; + procedure VisibilityProtected; override; + procedure VisibilityPublic; override; + procedure VisibilityPublished; override; + procedure VisibilityStrictPrivate; override; + procedure VisibilityStrictProtected; override; + procedure WhileStatement; override; + procedure WithExpressionList; override; + procedure WithStatement; override; + + procedure AttributeSections; override; + procedure Attribute; override; + procedure AttributeName; override; + procedure AttributeArguments; override; + procedure PositionalArgument; override; + procedure NamedArgument; override; + procedure AttributeArgumentName; override; + procedure AttributeArgumentExpression; override; + public + constructor Create; override; + destructor Destroy; override; + function Run(SourceStream: TStream): TSyntaxNode; reintroduce; overload; virtual; + class function Run(const FileName: string; InterfaceOnly: Boolean = False; + IncludeHandler: IIncludeHandler = nil; + OnHandleString: TStringEvent = nil): TSyntaxNode; reintroduce; overload; static; + property Comments: TObjectList<TCommentNode> read FComments; + end; + +implementation + +uses + TypInfo; + +{$IFDEF FPC} + type + TStringStreamHelper = class helper for TStringStream + class function Create: TStringStream; overload; + procedure LoadFromFile(const FileName: string); + end; + + { TStringStreamHelper } + + class function TStringStreamHelper.Create: TStringStream; + begin + Result := TStringStream.Create(''); + end; + + procedure TStringStreamHelper.LoadFromFile(const FileName: string); + var + Strings: TStringList; + begin + Strings := TStringList.Create; + try + Strings.LoadFromFile(FileName); + Strings.SaveToStream(Self); + finally + FreeAndNil(Strings); + end; + end; +{$ENDIF} + +// do not use const strings here to prevent allocating new strings every time + +type + TAttributeValue = (atAsm, atTrue, atFunction, atProcedure, atClassOf, atClass, + atConst, atConstructor, atDestructor, atEnum, atInterface, atNil, atNumeric, + atOut, atPointer, atName, atString, atSubRange, atVar, atDispInterface); + +var + AttributeValues: array[TAttributeValue] of string; + +procedure InitAttributeValues; +var + value: TAttributeValue; +begin + for value := Low(TAttributeValue) to High(TAttributeValue) do + AttributeValues[value] := Copy(LowerCase(GetEnumName(TypeInfo(TAttributeValue), Ord(value))), 3); +end; + +procedure AssignLexerPositionToNode(const Lexer: TPasLexer; const Node: TSyntaxNode); +begin + Node.LineSeq := Lexer.PosXY.LineSeq; + Node.Col := Lexer.PosXY.X; + Node.Line := Lexer.PosXY.Y; + Node.FileName := Lexer.FileName; +end; + +{ TNodeStack } + +function TNodeStack.AddChild(Typ: TSyntaxNodeType): TSyntaxNode; +begin + Result := FStack.Peek.AddChild(TSyntaxNode.Create(Typ)); + AssignLexerPositionToNode(FLexer, Result); +end; + +function TNodeStack.AddChild(Node: TSyntaxNode): TSyntaxNode; +begin + Result := FStack.Peek.AddChild(Node); +end; + +function TNodeStack.AddValuedChild(Typ: TSyntaxNodeType; + const Value: string): TSyntaxNode; +begin + Result := FStack.Peek.AddChild(TValuedSyntaxNode.Create(Typ)); + AssignLexerPositionToNode(FLexer, Result); + + TValuedSyntaxNode(Result).Value := Value; +end; + +procedure TNodeStack.Clear; +begin + FStack.Clear; +end; + +constructor TNodeStack.Create(Lexer: TPasLexer); +begin + FLexer := Lexer; + FStack := TStack<TSyntaxNode>.Create; +end; + +destructor TNodeStack.Destroy; +begin + FStack.Free; + inherited; +end; + +function TNodeStack.GetCount: Integer; +begin + Result := FStack.Count; +end; + +function TNodeStack.Peek: TSyntaxNode; +begin + Result := FStack.Peek; +end; + +function TNodeStack.Pop: TSyntaxNode; +begin + Result := FStack.Pop; +end; + +function TNodeStack.Push(Node: TSyntaxNode): TSyntaxNode; +begin + FStack.Push(Node); + Result := Node; + AssignLexerPositionToNode(FLexer, Result); +end; + +function TNodeStack.PushCompoundSyntaxNode(Typ: TSyntaxNodeType): TSyntaxNode; +begin + Result := Push(Peek.AddChild(TCompoundSyntaxNode.Create(Typ))); +end; + +function TNodeStack.PushValuedNode(Typ: TSyntaxNodeType; + const Value: string): TSyntaxNode; +begin + Result := Push(Peek.AddChild(TValuedSyntaxNode.Create(Typ))); + TValuedSyntaxNode(Result).Value := Value; +end; + +function TNodeStack.Push(Typ: TSyntaxNodeType): TSyntaxNode; +begin + Result := FStack.Peek.AddChild(TSyntaxNode.Create(Typ)); + Push(Result); +end; + +{ TPasSyntaxTreeBuilder } + +procedure TPasSyntaxTreeBuilder.AccessSpecifier; +begin + case ExID of + ptRead: + FStack.Push(ntRead); + ptWrite: + FStack.Push(ntWrite); + else + FStack.Push(ntUnknown); + end; + try + inherited AccessSpecifier; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.AdditiveOperator; +begin + case TokenID of + ptMinus: FStack.AddChild(ntSub); + ptOr: FStack.AddChild(ntOr); + ptPlus: FStack.AddChild(ntAdd); + ptXor: FStack.AddChild(ntXor); + end; + + inherited; +end; + +procedure TPasSyntaxTreeBuilder.AddressOp; +begin + FStack.Push(ntAddr); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.AlignmentParameter; +begin + FStack.Push(ntAlignmentParam); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.AnonymousMethod; +begin + FStack.Push(ntAnonymousMethod); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ArrayBounds; +begin + FStack.Push(ntBounds); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ArrayConstant; +begin + FStack.Push(ntExpressions); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ArrayDimension; +begin + FStack.Push(ntDimension); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.AsmStatement; +begin + FStack.PushCompoundSyntaxNode(ntStatements).SetAttribute(anType, AttributeValues[atAsm]); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.AsOp; +begin + FStack.AddChild(ntAs); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.AssignOp; +begin + FStack.AddChild(ntAssign); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.AtExpression; +begin + FStack.Push(ntAt); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.Attribute; +begin + FStack.Push(ntAttribute); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.AttributeArgumentExpression; +begin + FStack.Push(ntValue); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.AttributeArgumentName; +begin + FStack.AddValuedChild(ntName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.AttributeArguments; +begin + FStack.Push(ntArguments); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.AttributeName; +begin + FStack.AddValuedChild(ntName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.AttributeSections; +begin + FStack.Push(ntAttributes); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.BuildExpressionTree( + ExpressionMethod: TTreeBuilderMethod); +var + RawExprNode: TSyntaxNode; + ExprNode: TSyntaxNode; + + NodeList: TList<TSyntaxNode>; + Node: TSyntaxNode; + Col, Line, LineSeq: Integer; + FileName: string; +begin + LineSeq := Lexer.PosXY.LineSeq; + Line := Lexer.PosXY.Y; + Col := Lexer.PosXY.X; + FileName := Lexer.FileName; + + RawExprNode := TSyntaxNode.Create(ntExpression); + try + FStack.Push(RawExprNode); + try + ExpressionMethod; + finally + FStack.Pop; + end; + + if RawExprNode.HasChildren then + begin + ExprNode := FStack.Push(ntExpression); + try + ExprNode.LineSeq := LineSeq; + ExprNode.Line := Line; + ExprNode.Col := Col; + ExprNode.FileName := FileName; + + NodeList := TList<TSyntaxNode>.Create; + try + for Node in RawExprNode.ChildNodes do + NodeList.Add(Node); + TExpressionTools.RawNodeListToTree(RawExprNode, NodeList, ExprNode); + finally + NodeList.Free; + end; + finally + FStack.Pop; + end; + end; + finally + RawExprNode.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.BuildParametersList( + ParametersListMethod: TTreeBuilderMethod); +var + Params, Temp: TSyntaxNode; + ParamList, Param, TypeInfo, ParamExpr: TSyntaxNode; + ParamKind: string; +begin + Params := TSyntaxNode.Create(ntUnknown); + try + FStack.Push(ntParameters); + + FStack.Push(Params); + try + ParametersListMethod; + finally + FStack.Pop; + end; + + for ParamList in Params.ChildNodes do + begin + TypeInfo := ParamList.FindNode(ntType); + ParamKind := ParamList.GetAttribute(anKind); + ParamExpr := ParamList.FindNode(ntExpression); + + for Param in ParamList.ChildNodes do + begin + if Param.Typ <> ntName then + Continue; + + Temp := FStack.Push(ntParameter); + if ParamKind <> '' then + Temp.SetAttribute(anKind, ParamKind); + + Temp.Col := Param.Col; + Temp.Line := Param.Line; + + FStack.AddChild(Param.Clone); + if Assigned(TypeInfo) then + FStack.AddChild(TypeInfo.Clone); + + if Assigned(ParamExpr) then + FStack.AddChild(ParamExpr.Clone); + + FStack.Pop; + end; + end; + FStack.Pop; + finally + Params.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.CaseElseStatement; +begin + FStack.Push(ntCaseElse); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.CaseLabel; +begin + FStack.Push(ntCaseLabel); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.CaseLabelList; +begin + FStack.Push(ntCaseLabels); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.CaseSelector; +begin + FStack.Push(ntCaseSelector); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.CaseStatement; +begin + FStack.Push(ntCase); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ClassClass; +begin + FStack.Peek.SetAttribute(anClass, AttributeValues[atTrue]); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.ClassField; +var + Fields, Temp: TSyntaxNode; + Field, TypeInfo, TypeArgs: TSyntaxNode; +begin + Fields := TSyntaxNode.Create(ntFields); + try + FStack.Push(Fields); + try + inherited; + finally + FStack.Pop; + end; + + TypeInfo := Fields.FindNode(ntType); + TypeArgs := Fields.FindNode(ntTypeArgs); + for Field in Fields.ChildNodes do + begin + if Field.Typ <> ntName then + Continue; + + Temp := FStack.Push(ntField); + try + Temp.AssignPositionFrom(Field); + + FStack.AddChild(Field.Clone); + TypeInfo := TypeInfo.Clone; + if Assigned(TypeArgs) then + TypeInfo.AddChild(TypeArgs.Clone); + FStack.AddChild(TypeInfo); + finally + FStack.Pop; + end; + end; + finally + Fields.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.ClassForward; +begin + FStack.Peek.SetAttribute(anForwarded, AttributeValues[atTrue]); + inherited ClassForward; +end; + +procedure TPasSyntaxTreeBuilder.ClassFunctionHeading; +begin + FStack.Peek.SetAttribute(anKind, AttributeValues[atFunction]); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.ClassHelper; +begin + FStack.Push(ntHelper); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ClassMethod; +begin + FStack.Peek.SetAttribute(anClass, AttributeValues[atTrue]); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.ClassMethodResolution; +begin + FStack.Push(ntResolutionClause); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ClassMethodHeading; +begin + FStack.PushCompoundSyntaxNode(ntMethod); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ClassProcedureHeading; +begin + FStack.Peek.SetAttribute(anKind, AttributeValues[atProcedure]); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.ClassProperty; +begin + FStack.Push(ntProperty); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ClassReferenceType; +begin + FStack.Push(ntType).SetAttribute(anType, AttributeValues[atClassof]); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ClassType; +begin + FStack.Push(ntType).SetAttribute(anType, AttributeValues[atClass]); + try + inherited; + finally + MoveMembersToVisibilityNodes(FStack.Pop); + end; +end; + +procedure TPasSyntaxTreeBuilder.MoveMembersToVisibilityNodes(TypeNode: TSyntaxNode); +var + child, vis: TSyntaxNode; + i: Integer; + extracted: Boolean; +begin + vis := nil; + i := 0; + while i < Length(TypeNode.ChildNodes) do + begin + child := TypeNode.ChildNodes[i]; + extracted := false; + if child.HasAttribute(anVisibility) then + vis := child + else if Assigned(vis) then + begin + TypeNode.ExtractChild(child); + vis.AddChild(child); + extracted := true; + end; + if not extracted then + inc(i); + end; +end; + +procedure TPasSyntaxTreeBuilder.ConstParameter; +begin + FStack.Push(ntParameters).SetAttribute(anKind, AttributeValues[atConst]); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ConstructorName; +var + Temp: TSyntaxNode; +begin + Temp := FStack.Peek; + Temp.SetAttribute(anKind, AttributeValues[atConstructor]); + Temp.SetAttribute(anName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.CompoundStatement; +begin + FStack.PushCompoundSyntaxNode(ntStatements); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ConstantDeclaration; +begin + FStack.Push(ntConstant); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ConstantExpression; +var + ExpressionMethod: TTreeBuilderMethod; +begin + ExpressionMethod := CallInheritedConstantExpression; + BuildExpressionTree(ExpressionMethod); +end; + +procedure TPasSyntaxTreeBuilder.CallInheritedFormalParameterList; +begin + inherited FormalParameterList; +end; + +procedure TPasSyntaxTreeBuilder.CallInheritedConstantExpression; +begin + inherited ConstantExpression; +end; + +procedure TPasSyntaxTreeBuilder.ConstantName; +begin + FStack.AddValuedChild(ntName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.ConstantValue; +begin + FStack.Push(ntValue); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ConstantValueTyped; +begin + FStack.Push(ntValue); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ConstraintList; +begin + FStack.Push(ntConstraints); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ClassConstraint; +begin + FStack.Push(ntClassConstraint); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ConstructorConstraint; +begin + FStack.Push(ntConstructorConstraint); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.RecordConstraint; +begin + FStack.Push(ntRecordConstraint); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.RecordAlignValue; +begin + FStack.Peek.SetAttribute(anAlign, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.ConstSection; +var + ConstSect, Temp: TSyntaxNode; + ConstList, Constant, TypeInfo, Value: TSyntaxNode; +begin + ConstSect := TSyntaxNode.Create(ntConstants); + try + FStack.Push(ntConstants); + + FStack.Push(ConstSect); + try + inherited ConstSection; + finally + FStack.Pop; + end; + + for ConstList in ConstSect.ChildNodes do + begin + TypeInfo := ConstList.FindNode(ntType); + Value := ConstList.FindNode(ntValue); + for Constant in ConstList.ChildNodes do + begin + if Constant.Typ <> ntName then + Continue; + + Temp := FStack.Push(ConstList.Typ); + try + Temp.AssignPositionFrom(Constant); + + FStack.AddChild(Constant.Clone); + if Assigned(TypeInfo) then + FStack.AddChild(TypeInfo.Clone); + FStack.AddChild(Value.Clone); + finally + FStack.Pop; + end; + end; + end; + FStack.Pop; + finally + ConstSect.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.ContainsClause; +begin + FStack.Push(ntContains); + try + inherited; + finally + FStack.Pop; + end; +end; + +constructor TPasSyntaxTreeBuilder.Create; +begin + inherited; + FStack := TNodeStack.Create(Lexer); + FComments := TObjectList<TCommentNode>.Create(True); + OnComment := DoOnComment; +end; + +function TPasSyntaxTreeBuilder.DequoteString(const S: string): string; +var + QuoteCount, I: Integer; +begin + QuoteCount := 0; + for I := Low(S) to High(S) do + if S[I] = '''' then + Inc(QuoteCount) + else + Break; + + if (QuoteCount = 1) or (QuoteCount mod 2 = 0) then + begin + Result := AnsiDequotedStr(S, ''''); + Exit; + end; + + Result := Copy(S, QuoteCount + 1, Length(S) - QuoteCount * 2); +end; + +destructor TPasSyntaxTreeBuilder.Destroy; +begin + FStack.Free; + FComments.Free; + inherited; +end; + +procedure TPasSyntaxTreeBuilder.DestructorName; +var + Temp: TSyntaxNode; +begin + Temp := FStack.Peek; + Temp.SetAttribute(anKind, AttributeValues[atDestructor]); + Temp.SetAttribute(anName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.DirectiveBinding; +var + token: string; +begin + token := Lexer.Token; + // Method bindings: + if SameText(token, 'override') or SameText(token, 'virtual') + or SameText(token, 'dynamic') + then + FStack.Peek.SetAttribute(anMethodBinding, token) + // Other directives + else if SameText(token, 'reintroduce') then + FStack.Peek.SetAttribute(anReintroduce, AttributeValues[atTrue]) + else if SameText(token, 'overload') then + FStack.Peek.SetAttribute(anOverload, AttributeValues[atTrue]) + else if SameText(token, 'abstract') then + FStack.Peek.SetAttribute(anAbstract, AttributeValues[atTrue]); + + inherited; +end; + +procedure TPasSyntaxTreeBuilder.DirectiveBindingMessage; +begin + FStack.Push(ntMessage); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.DirectiveCalling; +begin + FStack.Peek.SetAttribute(anCallingConvention, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.DirectiveInline; +begin + FStack.Peek.SetAttribute(anInline, AttributeValues[atTrue]); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.DispInterfaceForward; +begin + FStack.Peek.SetAttribute(anForwarded, AttributeValues[atTrue]); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.DotOp; +begin + FStack.AddChild(ntDot); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.ElseExpression; +begin + FStack.Push(ntElse); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ElseStatement; +begin + FStack.Push(ntElse); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.EmptyStatement; +begin + FStack.Push(ntEmptyStatement); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.EnumeratedType; +var + TypeNode: TSyntaxNode; +begin + TypeNode := FStack.Push(ntType); + try + TypeNode.SetAttribute(anName, AttributeValues[atEnum]); + if ScopedEnums then + TypeNode.SetAttribute(anVisibility, 'scoped'); + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ExceptBlock; +begin + FStack.Push(ntExcept); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ExceptionBlockElseBranch; +begin + FStack.Push(ntElse); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ExceptionHandler; +begin + FStack.Push(ntExceptionHandler); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ExceptionVariable; +begin + FStack.Push(ntVariable); + FStack.AddValuedChild(ntName, Lexer.Token); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ExportedHeading; +begin + FStack.PushCompoundSyntaxNode(ntMethod); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ExportsClause; +begin + FStack.Push(ntExports); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ExportsElement; +begin + FStack.Push(ntElement); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ExportsName; +var + NamesNode: TSyntaxNode; +begin + NamesNode := TSyntaxNode.Create(ntUnknown); + try + FStack.Push(NamesNode); + try + inherited; + finally + FStack.Pop; + end; + + FStack.Peek.SetAttribute(anName, NodeListToString(NamesNode)); + finally + NamesNode.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.ExportsNameId; +begin + FStack.AddChild(ntUnknown).SetAttribute(anName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.Expression; +var + ExpressionMethod: TTreeBuilderMethod; +begin + ExpressionMethod := CallInheritedExpression; + BuildExpressionTree(ExpressionMethod); +end; + +procedure TPasSyntaxTreeBuilder.SetCurrentCompoundNodesEndPosition; +var + Temp: TCompoundSyntaxNode; +begin + Temp := TCompoundSyntaxNode(FStack.Peek); + Temp.EndCol := Lexer.PosXY.X; + Temp.EndLine := Lexer.PosXY.Y; + Temp.FileName := Lexer.FileName; +end; + +procedure TPasSyntaxTreeBuilder.CallInheritedExpression; +begin + inherited Expression; +end; + +procedure TPasSyntaxTreeBuilder.CallInheritedPropertyParameterList; +begin + inherited PropertyParameterList; +end; + +procedure TPasSyntaxTreeBuilder.ExpressionList; +begin + FStack.Push(ntExpressions); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ExternalDirective; +begin + FStack.Push(ntExternal); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.FieldName; +begin + FStack.AddValuedChild(ntName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.FinalizationSection; +begin + FStack.PushCompoundSyntaxNode(ntFinalization); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.FinallyBlock; +begin + FStack.Push(ntFinally); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.FormalParameterList; +var + TreeBuilderMethod: TTreeBuilderMethod; +begin + TreeBuilderMethod := CallInheritedFormalParameterList; + BuildParametersList(TreeBuilderMethod); +end; + +procedure TPasSyntaxTreeBuilder.ForStatement; +begin + FStack.Push(ntFor); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ForStatementDownTo; +begin + FStack.Push(ntDownTo); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ForStatementFrom; +begin + FStack.Push(ntFrom); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ForStatementIn; +begin + FStack.Push(ntIn); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ForStatementTo; +begin + FStack.Push(ntTo); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.FunctionHeading; +begin + FStack.Peek.SetAttribute(anKind, AttributeValues[atFunction]); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.FunctionMethodName; +begin + FStack.AddValuedChild(ntName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.FunctionProcedureName; +var + ChildNode, NameNode, TypeParam, TypeNode, Temp: TSyntaxNode; + FullName, TypeParams: string; +begin + FStack.Push(ntName); + NameNode := FStack.Peek; + try + inherited; + for ChildNode in NameNode.ChildNodes do + begin + if ChildNode.Typ = ntTypeParams then + begin + TypeParams := ''; + + for TypeParam in ChildNode.ChildNodes do + begin + TypeNode := TypeParam.FindNode(ntType); + if Assigned(TypeNode) then + begin + if TypeParams <> '' then + TypeParams := TypeParams + ','; + TypeParams := TypeParams + TypeNode.GetAttribute(anName); + end; + end; + + FullName := FullName + '<' + TypeParams + '>'; + Continue; + end; + + if FullName <> '' then + FullName := FullName + '.'; + FullName := FullName + TValuedSyntaxNode(ChildNode).Value; + end; + finally + FStack.Pop; + Temp := FStack.Peek; + DoHandleString(FullName); + Temp.SetAttribute(anName, FullName); + Temp.DeleteChild(NameNode); + end; +end; + +procedure TPasSyntaxTreeBuilder.GotoStatement; +begin + FStack.Push(ntGoto); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.Identifier; +begin + FStack.AddChild(ntIdentifier).SetAttribute(anName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.TernaryOp; +begin + FStack.Push(ntTernaryOp); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.IfStatement; +begin + FStack.Push(ntIf); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ImplementationSection; +begin + FStack.PushCompoundSyntaxNode(ntImplementation); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ImplementsSpecifier; +begin + FStack.Push(ntImplements); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.IndexOp; +begin + FStack.Push(ntIndexed); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.IndexSpecifier; +begin + FStack.Push(ntIndex); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.InheritedStatement; +begin + FStack.Push(ntInherited); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.InheritedVariableReference; +begin + FStack.Push(ntInherited); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.InitializationSection; +begin + FStack.PushCompoundSyntaxNode(ntInitialization); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.InlineVarDeclaration; +begin + FStack.Push(ntVariables); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.InlineVarSection; +var + VarSect, Variables, Expression: TSyntaxNode; +begin + VarSect := TSyntaxNode.Create(ntUnknown); + try + Variables := FStack.Push(ntVariables); + + FStack.Push(VarSect); + try + inherited InlineVarSection; + finally + FStack.Pop; + end; + RearrangeVarSection(VarSect); + Expression := VarSect.FindNode(ntExpression); + if Assigned(Expression) then + Variables.AddChild(ntAssign).AddChild(Expression.Clone); + + FStack.Pop; + finally + VarSect.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.InterfaceForward; +begin + FStack.Peek.SetAttribute(anForwarded, AttributeValues[atTrue]); + inherited InterfaceForward; +end; + +procedure TPasSyntaxTreeBuilder.InterfaceGUID; +begin + FStack.Push(ntGuid); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.InterfaceSection; +begin + FStack.PushCompoundSyntaxNode(ntInterface); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.InterfaceType; +begin + case TokenID of + ptInterface: + FStack.Push(ntType).SetAttribute(anType, AttributeValues[atInterface]); + ptDispInterface: + FStack.Push(ntType).SetAttribute(anType, AttributeValues[atDispInterface]); + end; + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.IsNotOp; +begin + FStack.AddChild(ntIsNot); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.LabelId; +begin + FStack.AddValuedChild(ntLabel, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.MainUsedUnitStatement; +var + NameNode, PathNode, PathLiteralNode, Temp: TSyntaxNode; +begin + FStack.Push(ntUnit); + try + inherited; + + NameNode := FStack.Peek.FindNode(ntUnit); + + if Assigned(NameNode) then + begin + Temp := FStack.Peek; + Temp.SetAttribute(anName, NameNode.GetAttribute(anName)); + Temp.DeleteChild(NameNode); + end; + + PathNode := FStack.Peek.FindNode(ntExpression); + if Assigned(PathNode) then + begin + FStack.Peek.ExtractChild(PathNode); + try + PathLiteralNode := PathNode.FindNode(ntLiteral); + + if PathLiteralNode is TValuedSyntaxNode then + FStack.Peek.SetAttribute(anPath, TValuedSyntaxNode(PathLiteralNode).Value); + finally + PathNode.Free; + end; + end; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.MainUsesClause; +begin + FStack.PushCompoundSyntaxNode(ntUses); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.MethodKind; +var + value: string; +begin + value := LowerCase(Lexer.Token); + DoHandleString(value); + FStack.Peek.SetAttribute(anKind, value); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.MultiplicativeOperator; +begin + case TokenID of + ptAnd: + FStack.AddChild(ntAnd); + ptDiv: + FStack.AddChild(ntDiv); + ptMod: + FStack.AddChild(ntMod); + ptShl: + FStack.AddChild(ntShl); + ptShr: + FStack.AddChild(ntShr); + ptSlash: + FStack.AddChild(ntFDiv); + ptStar: + FStack.AddChild(ntMul); + else + FStack.AddChild(ntUnknown); + end; + + inherited; +end; + +procedure TPasSyntaxTreeBuilder.NamedArgument; +begin + FStack.Push(ntNamedArgument); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.NilToken; +begin + FStack.AddChild(ntLiteral).SetAttribute(anType, AttributeValues[atNil]); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.NotInOp; +begin + FStack.AddChild(ntNotIn); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.NotOp; +begin + FStack.AddChild(ntNot); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.Number; +var + Node: TSyntaxNode; +begin + Node := FStack.AddValuedChild(ntLiteral, Lexer.Token); + Node.SetAttribute(anType, AttributeValues[atNumeric]); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.ObjectNameOfMethod; +begin + FStack.AddValuedChild(ntName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.DoOnComment(Sender: TObject; const Text: string); +var + Node: TCommentNode; +begin + case TokenID of + ptAnsiComment: Node := TCommentNode.Create(ntAnsiComment); + ptBorComment: Node := TCommentNode.Create(ntBorComment); + ptSlashesComment: Node := TCommentNode.Create(ntSlashesComment); + else + raise EParserException.Create(Lexer.PosXY.Y, Lexer.PosXY.X, Lexer.FileName, 'Invalid comment type'); + end; + + AssignLexerPositionToNode(Lexer, Node); + Node.Text := Text; + + FComments.Add(Node); +end; + +procedure TPasSyntaxTreeBuilder.ParserMessage(Sender: TObject; + const Typ: TMessageEventType; const Msg: string; X, Y: Integer); +begin + if Typ = TMessageEventType.meError then + raise EParserException.Create(Y, X, Lexer.FileName, Msg); +end; + +procedure TPasSyntaxTreeBuilder.OutParameter; +begin + FStack.Push(ntParameters).SetAttribute(anKind, AttributeValues[atOut]); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ParameterFormal; +begin + FStack.Push(ntParameters); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ParameterName; +begin + FStack.AddValuedChild(ntName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.PointerSymbol; +begin + FStack.AddChild(ntDeref); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.PointerType; +begin + FStack.Push(ntType).SetAttribute(anType, AttributeValues[atPointer]); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.PositionalArgument; +begin + FStack.Push(ntPositionalArgument); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ProceduralType; +begin + FStack.Push(ntType).SetAttribute(anName, Lexer.Token); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ProcedureDeclarationSection; +begin + FStack.PushCompoundSyntaxNode(ntMethod); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ProcedureHeading; +begin + FStack.Peek.SetAttribute(anKind, AttributeValues[atProcedure]); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.ProcedureProcedureName; +begin + FStack.Peek.SetAttribute(anName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.PropertyName; +begin + FStack.Peek.SetAttribute(anName, Lexer.Token); + inherited PropertyName; +end; + +procedure TPasSyntaxTreeBuilder.PropertyParameterList; +var + TreeBuilderMethod: TTreeBuilderMethod; +begin + TreeBuilderMethod := CallInheritedPropertyParameterList; + BuildParametersList(TreeBuilderMethod); +end; + +procedure TPasSyntaxTreeBuilder.RaiseStatement; +begin + FStack.Push(ntRaise); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.RecordFieldConstant; +var + Node: TSyntaxNode; +begin + Node := FStack.PushValuedNode(ntField, Lexer.Token); + try + Node.SetAttribute(anType, AttributeValues[atName]); + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.RecordType; +begin + inherited RecordType; + MoveMembersToVisibilityNodes(FStack.Peek); +end; + +procedure TPasSyntaxTreeBuilder.RelativeOperator; +begin + case TokenID of + ptAs: + FStack.AddChild(ntAs); + ptEqual: + FStack.AddChild(ntEqual); + ptGreater: + FStack.AddChild(ntGreater); + ptGreaterEqual: + FStack.AddChild(ntGreaterEqual); + ptIn: + FStack.AddChild(ntIn); + ptIs: + FStack.AddChild(ntIs); + ptLower: + FStack.AddChild(ntLower); + ptLowerEqual: + FStack.AddChild(ntLowerEqual); + ptNotEqual: + FStack.AddChild(ntNotEqual); + else + FStack.AddChild(ntUnknown); + end; + + inherited; +end; + +procedure TPasSyntaxTreeBuilder.RepeatStatement; +begin + FStack.Push(ntRepeat); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.RequiresClause; +begin + FStack.Push(ntRequires); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.RequiresIdentifier; +var + NamesNode: TSyntaxNode; +begin + NamesNode := TSyntaxNode.Create(ntUnknown); + try + FStack.Push(NamesNode); + try + inherited; + finally + FStack.Pop; + end; + + FStack.AddChild(ntPackage).SetAttribute(anName, NodeListToString(NamesNode)); + finally + NamesNode.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.RequiresIdentifierId; +begin + FStack.AddChild(ntUnknown).SetAttribute(anName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.ResourceDeclaration; +begin + FStack.Push(ntResourceString); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ResourceValue; +begin + FStack.Push(ntValue); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ReturnType; +begin + FStack.Push(ntReturnType); + try + inherited; + finally + FStack.Pop + end; +end; + +procedure TPasSyntaxTreeBuilder.RoundClose; +begin + FStack.AddChild(ntRoundClose); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.RoundOpen; +begin + FStack.AddChild(ntRoundOpen); + inherited; +end; + +class function TPasSyntaxTreeBuilder.Run(const FileName: string; + InterfaceOnly: Boolean; IncludeHandler: IIncludeHandler; + OnHandleString: TStringEvent): TSyntaxNode; +var + Stream: TStringStream; + Builder: TPasSyntaxTreeBuilder; +begin + Stream := TStringStream.Create; + try + Stream.LoadFromFile(FileName); + Builder := TPasSyntaxTreeBuilder.Create; + Builder.InterfaceOnly := InterfaceOnly; + Builder.OnHandleString := OnHandleString; + try + Builder.InitDefinesDefinedByCompiler; + Builder.IncludeHandler := IncludeHandler; + Result := Builder.Run(Stream); + finally + Builder.Free; + end; + finally + Stream.Free; + end; +end; + +function TPasSyntaxTreeBuilder.Run(SourceStream: TStream): TSyntaxNode; +begin + Result := TSyntaxNode.Create(ntUnit); + try + FStack.Clear; + FStack.Push(Result); + try + self.OnMessage := ParserMessage; + inherited Run('', SourceStream); + finally + FStack.Pop; + end; + except + on E: EParserException do + raise ESyntaxTreeException.Create(E.Line, E.Col, Lexer.FileName, E.Message, Result); + on E: ESyntaxError do + raise ESyntaxTreeException.Create(E.PosXY.X, E.PosXY.Y, Lexer.FileName, E.Message, Result); + else + FreeAndNil(Result); + raise; + end; + + Assert(FStack.Count = 0); +end; + +function TPasSyntaxTreeBuilder.NodeListToString(NamesNode: TSyntaxNode): string; +var + NamePartNode: TSyntaxNode; +begin + Result := ''; + for NamePartNode in NamesNode.ChildNodes do + begin + if Result <> '' then + Result := Result + '.'; + Result := Result + NamePartNode.GetAttribute(anName); + end; + DoHandleString(Result); +end; + +procedure TPasSyntaxTreeBuilder.SetConstructor; +begin + FStack.Push(ntSet); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.SetElement; +begin + FStack.Push(ntElement); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.SimpleStatement; +var + RawStatement, Temp: TSyntaxNode; + Node, LHS, RHS: TSyntaxNode; + NodeList: TList<TSyntaxNode>; + I, AssignIdx: Integer; + Position: TTokenPoint; + FileName: string; + LineSeq: Integer; +begin + LineSeq := Lexer.PosXY.LineSeq; + Position := Lexer.PosXY; + FileName := Lexer.FileName; + + RawStatement := TSyntaxNode.Create(ntStatement); + try + FStack.Push(RawStatement); + try + inherited; + finally + FStack.Pop; + end; + + if not RawStatement.HasChildren then + Exit; + + if RawStatement.FindNode(ntAssign) <> nil then + begin + Temp := FStack.Push(ntAssign); + try + Temp.LineSeq := LineSeq; + Temp.Col := Position.X; + Temp.Line := Position.Y; + Temp.FileName := FileName; + + NodeList := TList<TSyntaxNode>.Create; + try + AssignIdx := -1; + for I := 0 to Length(RawStatement.ChildNodes) - 1 do + begin + if RawStatement.ChildNodes[I].Typ = ntAssign then + begin + AssignIdx := I; + Break; + end; + NodeList.Add(RawStatement.ChildNodes[I]); + end; + + if NodeList.Count = 0 then + raise EParserException.Create(Position.Y, Position.X, Lexer.FileName, 'Illegal expression'); + + LHS := FStack.AddChild(ntLHS); + LHS.AssignPositionFrom(NodeList[0]); + + TExpressionTools.RawNodeListToTree(RawStatement, NodeList, LHS); + + NodeList.Clear; + + for I := AssignIdx + 1 to Length(RawStatement.ChildNodes) - 1 do + NodeList.Add(RawStatement.ChildNodes[I]); + + if NodeList.Count = 0 then + raise EParserException.Create(Position.Y, Position.X, Lexer.FileName, 'Illegal expression'); + + RHS := FStack.AddChild(ntRHS); + RHS.AssignPositionFrom(NodeList[0]); + + TExpressionTools.RawNodeListToTree(RawStatement, NodeList, RHS); + finally + NodeList.Free; + end; + finally + FStack.Pop; + end; + end else + begin + Temp := FStack.Push(ntCall); + try + Temp.Col := Position.X; + Temp.Line := Position.Y; + + NodeList := TList<TSyntaxNode>.Create; + try + for Node in RawStatement.ChildNodes do + NodeList.Add(Node); + TExpressionTools.RawNodeListToTree(RawStatement, NodeList, FStack.Peek); + finally + NodeList.Free; + end; + finally + FStack.Pop; + end; + end; + finally + RawStatement.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.SimpleType; +begin + FStack.Push(ntType).SetAttribute(anName, Lexer.Token); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.StatementList; +begin + FStack.PushCompoundSyntaxNode(ntStatements); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.StorageDefault; +begin + FStack.Push(ntDefault); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.StringConst; +var + StrConst: TSyntaxNode; + Literal, Node: TSyntaxNode; + Str: string; +begin + StrConst := TSyntaxNode.Create(ntUnknown); + try + FStack.Push(StrConst); + try + inherited; + finally + FStack.Pop; + end; + + Str := ''; + for Literal in StrConst.ChildNodes do + Str := Str + TValuedSyntaxNode(Literal).Value; + finally + StrConst.Free; + end; + + DoHandleString(Str); + Node := FStack.AddValuedChild(ntLiteral, Str); + Node.SetAttribute(anType, AttributeValues[atString]); +end; + +procedure TPasSyntaxTreeBuilder.StringConstSimple; +begin + //TODO support ptAsciiChar + FStack.AddValuedChild(ntLiteral, DequoteString(Lexer.Token)); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.StringStatement; +begin + FStack.AddChild(ntType).SetAttribute(anName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.StructuredType; +begin + FStack.Push(ntType).SetAttribute(anType, Lexer.Token); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.SubrangeType; +begin + FStack.Push(ntType).SetAttribute(anName, AttributeValues[atSubRange]); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ThenExpression; +begin + FStack.Push(ntThen); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.ThenStatement; +begin + FStack.Push(ntThen); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.TryStatement; +begin + FStack.Push(ntTry); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.TypeArgs; +begin + FStack.Push(ntTypeArgs); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.TypeDeclaration; +begin + FStack.PushCompoundSyntaxNode(ntTypeDecl).SetAttribute(anName, Lexer.Token); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.TypeId; +var + TypeNode, InnerTypeNode, SubNode: TSyntaxNode; + TypeName, InnerTypeName: string; + i: integer; +begin + TypeNode := FStack.Push(ntType); + try + inherited; + + InnerTypeName := ''; + InnerTypeNode := TypeNode.FindNode(ntType); + if Assigned(InnerTypeNode) then + begin + InnerTypeName := InnerTypeNode.GetAttribute(anName); + for SubNode in InnerTypeNode.ChildNodes do + TypeNode.AddChild(SubNode.Clone); + + TypeNode.DeleteChild(InnerTypeNode); + end; + + TypeName := ''; + for i := Length(TypeNode.ChildNodes) - 1 downto 0 do + begin + SubNode := TypeNode.ChildNodes[i]; + if SubNode.Typ = ntType then + begin + if TypeName <> '' then + TypeName := '.' + TypeName; + + TypeName := SubNode.GetAttribute(anName) + TypeName; + TypeNode.DeleteChild(SubNode); + end; + end; + + if TypeName <> '' then + TypeName := '.' + TypeName; + TypeName := InnerTypeName + TypeName; + + DoHandleString(TypeName); + TypeNode.SetAttribute(anName, TypeName); + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.TypeParamDecl; +var + OriginTypeParamNode, NewTypeParamNode, Constraints, TypeNode: TSyntaxNode; + TypeNodeCount: integer; + TypeNodesToDelete: TList<TSyntaxNode>; +begin + OriginTypeParamNode := FStack.Push(ntTypeParam); + try + inherited; + finally + FStack.Pop; + end; + + Constraints := OriginTypeParamNode.FindNode(ntConstraints); + TypeNodeCount := 0; + TypeNodesToDelete := TList<TSyntaxNode>.Create; + try + for TypeNode in OriginTypeParamNode.ChildNodes do + begin + if TypeNode.Typ = ntType then + begin + inc(TypeNodeCount); + if TypeNodeCount > 1 then + begin + NewTypeParamNode := FStack.Push(ntTypeParam); + try + NewTypeParamNode.AddChild(TypeNode.Clone); + if Assigned(Constraints) then + NewTypeParamNode.AddChild(Constraints.Clone); + TypeNodesToDelete.Add(TypeNode); + finally + FStack.Pop; + end; + end; + end; + end; + + for TypeNode in TypeNodesToDelete do + OriginTypeParamNode.DeleteChild(TypeNode); + finally + TypeNodesToDelete.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.TypeParams; +begin + FStack.Push(ntTypeParams); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.TypeSection; +begin + FStack.Push(ntTypeSection); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.TypeSimple; +begin + FStack.Push(ntType).SetAttribute(anName, Lexer.Token); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.UnaryMinus; +begin + FStack.AddChild(ntUnaryMinus); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.UnitFile; +var + Temp: TSyntaxNode; +begin + Temp := FStack.Peek; + AssignLexerPositionToNode(Lexer, Temp); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.UnitId; +begin + FStack.AddChild(ntUnknown).SetAttribute(anName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.UnitName; +var + NamesNode: TSyntaxNode; +begin + NamesNode := TSyntaxNode.Create(ntUnknown); + try + FStack.Push(NamesNode); + try + inherited; + finally + FStack.Pop; + end; + + FStack.Peek.SetAttribute(anName, NodeListToString(NamesNode)); + finally + NamesNode.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.UsedUnitName; +var + NamesNode, UnitNode: TSyntaxNode; + Position: TTokenPoint; + FileName: string; + LineSeq: Integer; +begin + LineSeq := Lexer.PosXY.LineSeq; + Position := Lexer.PosXY; + FileName := Lexer.FileName; + + NamesNode := TSyntaxNode.Create(ntUnit); + try + FStack.Push(NamesNode); + try + inherited; + finally + FStack.Pop; + end; + + UnitNode := FStack.AddChild(ntUnit); + UnitNode.SetAttribute(anName, NodeListToString(NamesNode)); + UnitNode.Col := Position.X; + UnitNode.Line := Position.Y; + UnitNode.FileName := FileName; + UnitNode.LineSeq := LineSeq; + finally + NamesNode.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.UsesClause; +begin + FStack.PushCompoundSyntaxNode(ntUses); + try + inherited; + SetCurrentCompoundNodesEndPosition; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.VarAbsolute; +begin + FStack.Push(ntAbsolute); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.VarDeclaration; +begin + FStack.Push(ntVariables); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.VarName; +begin + FStack.AddValuedChild(ntName, Lexer.Token); + inherited; +end; + +procedure TPasSyntaxTreeBuilder.VarParameter; +begin + FStack.Push(ntParameters).SetAttribute(anKind, AttributeValues[atVar]); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.VarSection; +var + VarSect: TSyntaxNode; +begin + VarSect := TSyntaxNode.Create(ntUnknown); + try + FStack.Push(ntVariables); + + FStack.Push(VarSect); + try + inherited VarSection; + finally + FStack.Pop; + end; + + RearrangeVarSection(VarSect); + FStack.Pop; + finally + VarSect.Free; + end; +end; + +procedure TPasSyntaxTreeBuilder.RearrangeVarSection(const VarSect: TSyntaxNode); +var + Temp: TSyntaxNode; + VarList, Variable, TypeInfo, ValueInfo: TSyntaxNode; +begin + for VarList in VarSect.ChildNodes do + begin + TypeInfo := VarList.FindNode(ntType); + ValueInfo := VarList.FindNode(ntValue); + for Variable in VarList.ChildNodes do + begin + if Variable.Typ <> ntName then + Continue; + Temp := FStack.Push(ntVariable); + try + Temp.AssignPositionFrom(Variable); + FStack.AddChild(Variable.Clone); + if Assigned(TypeInfo) then + FStack.AddChild(TypeInfo.Clone); + if Assigned(ValueInfo) then + FStack.AddChild(ValueInfo.Clone) + else + begin + Temp := VarList.FindNode([ntAbsolute, ntValue, ntExpression, ntIdentifier]); + if Assigned(Temp) then + FStack.AddChild(ntAbsolute).AddChild(Temp.Clone); + end; + finally + FStack.Pop; + end; + end; + end; +end; + +procedure TPasSyntaxTreeBuilder.VisibilityStrictPrivate; +var + Temp: TSyntaxNode; +begin + Temp := FStack.Push(ntStrictPrivate); + try + Temp.SetAttribute(anVisibility, AttributeValues[atTrue]); + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.VisibilityPrivate; +var + Temp: TSyntaxNode; +begin + Temp := FStack.Push(ntPrivate); + try + Temp.SetAttribute(anVisibility, AttributeValues[atTrue]); + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.VisibilityStrictProtected; +var + Temp: TSyntaxNode; +begin + Temp := FStack.Push(ntStrictProtected); + try + Temp.SetAttribute(anVisibility, AttributeValues[atTrue]); + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.VisibilityProtected; +var + Temp: TSyntaxNode; +begin + Temp := FStack.Push(ntProtected); + try + Temp.SetAttribute(anVisibility, AttributeValues[atTrue]); + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.VisibilityPublic; +var + Temp: TSyntaxNode; +begin + Temp := FStack.Push(ntPublic); + try + Temp.SetAttribute(anVisibility, AttributeValues[atTrue]); + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.VisibilityPublished; +var + Temp: TSyntaxNode; +begin + Temp := FStack.Push(ntPublished); + try + Temp.SetAttribute(anVisibility, AttributeValues[atTrue]); + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.WhileStatement; +begin + FStack.Push(ntWhile); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.WithExpressionList; +begin + FStack.Push(ntExpressions); + try + inherited; + finally + FStack.Pop; + end; +end; + +procedure TPasSyntaxTreeBuilder.WithStatement; +begin + FStack.Push(ntWith); + try + inherited; + finally + FStack.Pop; + end; +end; + +{ ESyntaxTreeException } + +constructor ESyntaxTreeException.Create(Line, Col: Integer; const FileName, Msg: string; + SyntaxTree: TSyntaxNode); +begin + inherited Create(Line, Col, FileName, Msg); + FSyntaxTree := SyntaxTree; +end; + +destructor ESyntaxTreeException.Destroy; +begin + FSyntaxTree.Free; + inherited; +end; + +initialization + InitAttributeValues; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Source/FreePascalSupport/Diagnostics.pas b/References/DelphiAST/Source/FreePascalSupport/Diagnostics.pas new file mode 100644 index 000000000..a5356b67a --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Diagnostics.pas @@ -0,0 +1,204 @@ +{$IFDEF FPC} + {$mode objfpc}{$H+} + {$modeswitch advancedrecords} +{$ENDIF} + +{$IFDEF MSWINDOWS} + {$IFNDEF WINDOWS} + {$DEFINE WINDOWS} + {$ENDIF WINDOWS} +{$ENDIF MSWINDOWS} + +unit Diagnostics; + +interface +uses + SysUtils + {$IFDEF LINUX} + ,unixtype, linux + {$ENDIF LINUX} + ; + +type + + { TStopWatch } + + TStopWatch = record + private + const + C_THOUSAND = 1000; + C_MILLION = C_THOUSAND * C_THOUSAND; + C_BILLION = C_THOUSAND * C_THOUSAND * C_THOUSAND; + {$IFDEF WINDOWS} + TicksPerMillisecond = 10000; + TicksPerSecond = 10000000; + {$ELSE} + TicksPerNanoSecond = 100; + TicksPerMilliSecond = 10000; + TicksPerSecond = C_BILLION div 100; + {$ENDIF} + Type + TBaseMesure = + {$IFDEF WINDOWS} + Int64; + {$ENDIF WINDOWS} + {$IFDEF LINUX} + TTimeSpec; + {$ENDIF LINUX} + strict private + class var FFrequency : Int64; + class var FIsHighResolution : Boolean; + class var TickFrequency: Double; + strict private + FElapsed : Int64; + FRunning : Boolean; + FStartPosition : TBaseMesure; + strict private + procedure CheckInitialization();inline; + function GetElapsedMilliseconds: Int64; + function GetElapsedTicks: Int64; + public + class function Create() : TStopWatch;static; + class function StartNew() : TStopWatch;static; + {$IFDEF WINDOWS}class function GetTimeStamp: Int64;static;{$ENDIF} + class property Frequency : Int64 read FFrequency; + class property IsHighResolution : Boolean read FIsHighResolution; + procedure Reset(); + procedure Start(); + procedure Stop(); + property ElapsedMilliseconds : Int64 read GetElapsedMilliseconds; + property ElapsedTicks : Int64 read GetElapsedTicks; + property IsRunning : Boolean read FRunning; + end; + +resourcestring + sStopWatchNotInitialized = 'The StopWatch is not initialized.'; + +implementation +{$IFDEF WINDOWS} +uses + Windows; +{$ENDIF WINDOWS} + +{ TStopWatch } + +class function TStopWatch.Create(): TStopWatch; +{$IFDEF LINUX} +var + r : TBaseMesure; +{$ENDIF LINUX} +begin + if (FFrequency = 0) then begin +{$IFDEF WINDOWS} + FIsHighResolution := QueryPerformanceFrequency(FFrequency); + if FIsHighResolution then begin + TickFrequency := 10000000 / FFrequency; + end else begin + FFrequency := TicksPerSecond; + TickFrequency := 1; + end; +{$ENDIF WINDOWS} +{$IFDEF LINUX} + FIsHighResolution := (clock_getres(CLOCK_MONOTONIC,@r) = 0); + FIsHighResolution := FIsHighResolution and (r.tv_nsec <> 0); + if (r.tv_nsec <> 0) then + FFrequency := C_BILLION div r.tv_nsec; +{$ENDIF LINUX} + end; + FillChar(Result,SizeOf(Result),0); +end; + +class function TStopWatch.StartNew() : TStopWatch; +begin + Result := TStopWatch.Create(); + Result.Start(); +end; + +procedure TStopWatch.CheckInitialization(); +begin + if (FFrequency = 0) then + raise Exception.Create(sStopWatchNotInitialized); +end; + +function TStopWatch.GetElapsedMilliseconds: Int64; +begin + {$IFDEF WINDOWS} + Result := ElapsedTicks; + if FIsHighResolution then + Result := Trunc(Result * TickFrequency); + + Result := Result div TicksPerMillisecond; + {$ENDIF WINDOWS} + {$IFDEF LINUX} + Result := FElapsed div C_MILLION; + {$ENDIF LINUX} +end; + +function TStopWatch.GetElapsedTicks: Int64; +begin + CheckInitialization(); +{$IFDEF WINDOWS} + Result := FElapsed; + if FRunning then + Result := Result + GetTimeStamp - FStartPosition; +{$ENDIF WINDOWS} +{$IFDEF LINUX} + Result := FElapsed div TicksPerNanoSecond; +{$ENDIF LINUX} +end; + +procedure TStopWatch.Reset(); +begin + Stop(); + FElapsed := 0; + FillChar(FStartPosition,SizeOf(FStartPosition),0); +end; + +procedure TStopWatch.Start(); +begin + if FRunning then + exit; + FRunning := True; +{$IFDEF WINDOWS} + FStartPosition := GetTimeStamp; +{$ENDIF WINDOWS} +{$IFDEF LINUX} + clock_gettime(CLOCK_MONOTONIC,@FStartPosition); +{$ENDIF LINUX} +end; + +procedure TStopWatch.Stop(); +var + locEnd : TBaseMesure; + s, n : Int64; +begin + if not FRunning then + exit; + FRunning := False; +{$IFDEF WINDOWS} + FElapsed := FElapsed + GetTimeStamp - FStartPosition; +{$ENDIF WINDOWS} +{$IFDEF LINUX} + clock_gettime(CLOCK_MONOTONIC,@locEnd); + if (locEnd.tv_nsec < FStartPosition.tv_nsec) then begin + s := locEnd.tv_sec - FStartPosition.tv_sec - 1; + n := C_BILLION + locEnd.tv_nsec - FStartPosition.tv_nsec; + end else begin + s := locEnd.tv_sec - FStartPosition.tv_sec; + n := locEnd.tv_nsec - FStartPosition.tv_nsec; + end; + FElapsed := FElapsed + (s * C_BILLION) + n; +{$ENDIF LINUX} +end; + +{$IFDEF WINDOWS} +class function TStopwatch.GetTimeStamp: Int64; +begin + if FIsHighResolution then + QueryPerformanceCounter(Result) + else + Result := GetTickCount * TicksPerMillisecond; +end; +{$ENDIF} + +end. diff --git a/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/.gitignore b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/.gitignore new file mode 100644 index 000000000..420520be3 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/.gitignore @@ -0,0 +1,3 @@ +compiled_junk/ +/Compiled +*.lps \ No newline at end of file diff --git a/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/FPC_StringBuilder.lpk b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/FPC_StringBuilder.lpk new file mode 100644 index 000000000..05393cf54 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/FPC_StringBuilder.lpk @@ -0,0 +1,39 @@ +<?xml version="1.0"?> +<CONFIG> + <Package Version="4"> + <PathDelim Value="\"/> + <Name Value="FPC_StringBuilder"/> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <SearchPaths> + <OtherUnitFiles Value="Src"/> + <UnitOutputDirectory Value="Compiled\$(TargetCPU)-$(TargetOS)\"/> + </SearchPaths> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Files Count="1"> + <Item1> + <Filename Value="Src\StringBuilderUnit.pas"/> + <UnitName Value="StringBuilderUnit"/> + </Item1> + </Files> + <Type Value="RunAndDesignTime"/> + <RequiredPkgs Count="1"> + <Item1> + <PackageName Value="FCL"/> + </Item1> + </RequiredPkgs> + <UsageOptions> + <UnitPath Value="$(PkgOutDir)"/> + </UsageOptions> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + </Package> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/FPC_StringBuilder.pas b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/FPC_StringBuilder.pas new file mode 100644 index 000000000..72cedea35 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/FPC_StringBuilder.pas @@ -0,0 +1,20 @@ +{ This file was automatically created by Lazarus. Do not edit! + This source is only used to compile and install the package. + } + +unit FPC_StringBuilder; + +interface + +uses + StringBuilderUnit, LazarusPackageIntf; + +implementation + +procedure Register; +begin +end; + +initialization + RegisterPackage('FPC_StringBuilder', @Register); +end. diff --git a/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/StringBuilderUnit.o b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/StringBuilderUnit.o new file mode 100644 index 000000000..6d59c6ab8 Binary files /dev/null and b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/StringBuilderUnit.o differ diff --git a/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/StringBuilderUnit.pas b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/StringBuilderUnit.pas new file mode 100644 index 000000000..e526b02af --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/StringBuilderUnit.pas @@ -0,0 +1,171 @@ +unit StringBuilderUnit; + +interface + +const + StringBuilderMemoryBlockLength = 1000; + +type + PStringBuilderMemoryBlock = ^TStringBuilderMemoryBlock; + TStringBuilderMemoryBlock = record + Data: array[0..StringBuilderMemoryBlockLength - 1] of byte; + Count: Cardinal; + Next: PStringBuilderMemoryBlock; + end; + + { TStringBuilder } + + TStringBuilder = class + protected + FHead, FTail: PStringBuilderMemoryBlock; + FTotalLength: Cardinal; + function Min(const a, b: Cardinal): Cardinal; + public + property Head: PStringBuilderMemoryBlock read FHead; + property Tail: PStringBuilderMemoryBlock read FTail; + property TotalLength: Cardinal read FTotalLength; + constructor Create; + procedure Add(const aString: string); overload; + procedure Add(const aStrings: array of string); overload; + procedure Append(const aString: string); + procedure AppendLine; + function ToString: string; + procedure Clean; + destructor Destroy; override; + end; + +implementation + +function CreateNewBlock: PStringBuilderMemoryBlock; +begin + New(result); + result^.Count := 0; + result^.Next := nil; +end; + +{ TStringBuilder } + +function TStringBuilder.Min(const a, b: Cardinal): Cardinal; +begin + if + a < b + then + result := a + else + result := b; +end; + +constructor TStringBuilder.Create; +begin + FHead := nil; + FTail := nil; + FTotalLength := 0; +end; + +procedure TStringBuilder.Add(const aString: string); +var + bytesLeftInString, bytesToWriteInCurrentBlock, bytesLeftInTailBlock: Cardinal; + positionInString: PChar; +begin + if + nil = Head + then + begin + FHead := CreateNewBlock; + FTail := Head; + end; + bytesLeftInString := Length(aString); + Inc(FTotalLength, bytesLeftInString); + positionInString := PChar(aString); + while + bytesLeftInString > 0 + do + begin + bytesLeftInTailBlock := StringBuilderMemoryBlockLength - Tail^.Count; + bytesToWriteInCurrentBlock := Min(bytesLeftInString, bytesLeftInTailBlock); + Move(positionInString^, Tail^.Data[Tail^.Count], bytesToWriteInCurrentBlock); + Inc(Tail^.Count, bytesToWriteInCurrentBlock); + Dec(bytesLeftInString, bytesToWriteInCurrentBlock); + Inc(positionInString, bytesToWriteInCurrentBlock); + if + bytesLeftInString > 0 + then + begin + Tail^.Next := CreateNewBlock; + FTail := Tail^.Next; + end; + end; +end; + +procedure TStringBuilder.Add(const aStrings: array of string); +var + i: Cardinal; +begin + for i := 0 to Length(aStrings) - 1 do + Add(aStrings[i]); +end; + +procedure TStringBuilder.Append(const aString: string); +begin + Add(aString); +end; + +procedure TStringBuilder.AppendLine; +begin + Add(#13#10); +end; + +function TStringBuilder.ToString: string; +var + currentBlock: PStringBuilderMemoryBlock; + currentCount: Cardinal; + currentResultPosition: PChar; +begin + if + Head <> nil + then + begin + SetLength(result, Totallength); + currentResultPosition := PChar(result); + currentBlock := Head; + while + currentBlock <> nil + do + begin + currentCount := currentBlock^.Count; + Move(currentBlock^.Data[0], currentResultPosition^, currentCount); + Inc(currentResultPosition, currentCount); + currentBlock := currentBlock^.Next; + end; + currentResultPosition^ := #0; + end + else + result := ''; +end; + +procedure TStringBuilder.Clean; +var + current, next: PStringBuilderMemoryBlock; +begin + current := Head; + while + current <> nil + do + begin + next := current^.Next; + Dispose(current); + current := next; + end; + FHead := nil; + FTail := nil; + FTotalLength := 0; +end; + +destructor TStringBuilder.Destroy; +begin + Clean; + inherited Destroy; +end; + +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/StringBuilderUnit.ppu b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/StringBuilderUnit.ppu new file mode 100644 index 000000000..85f532be2 Binary files /dev/null and b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/StringBuilderUnit.ppu differ diff --git a/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_001/Test_001.lpi b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_001/Test_001.lpi new file mode 100644 index 000000000..a71c363bd --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_001/Test_001.lpi @@ -0,0 +1,82 @@ +<?xml version="1.0"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="Test_001"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <RequiredPackages Count="1"> + <Item1> + <PackageName Value="FPC_StringBuilder"/> + </Item1> + </RequiredPackages> + <Units Count="1"> + <Unit0> + <Filename Value="Test_001.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="Test_001"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="..\..\..\Bin\Test\Test_001"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="compiled_junk\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Debugging> + <UseHeaptrc Value="True"/> + </Debugging> + </Linking> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_001/Test_001.lpr b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_001/Test_001.lpr new file mode 100644 index 000000000..bf7f8d18c --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_001/Test_001.lpr @@ -0,0 +1,20 @@ +program Test_001; + +uses + StringBuilderUnit; + +var + s: TStringBuilder; + +begin + s := TStringBuilder.Create; + s.Add('Foo'); + s.Add(' '); + s.Add('Bar'); + s.Clean; + s.Add('FFFUUU'); + s.Add('<BR>'); + WriteLN('"', s.ToString, '"'); + s.Free; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_002_Performance/Test_002.lpi b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_002_Performance/Test_002.lpi new file mode 100644 index 000000000..5e39bebb5 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_002_Performance/Test_002.lpi @@ -0,0 +1,87 @@ +<?xml version="1.0"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="Test_002"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <RequiredPackages Count="1"> + <Item1> + <PackageName Value="FPC_StringBuilder"/> + </Item1> + </RequiredPackages> + <Units Count="1"> + <Unit0> + <Filename Value="Test_002.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="Test_002"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="..\..\..\Bin\Test\Test_002"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="compiled_junk\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <CodeGeneration> + <Optimizations> + <OptimizationLevel Value="3"/> + </Optimizations> + </CodeGeneration> + <Linking> + <Debugging> + <UseHeaptrc Value="True"/> + </Debugging> + </Linking> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_002_Performance/Test_002.lpr b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_002_Performance/Test_002.lpr new file mode 100644 index 000000000..bb18ba70e --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/FPC_StringBuilder/Src/Test/Test_002_Performance/Test_002.lpr @@ -0,0 +1,70 @@ +program Test_002; + +uses + SysUtils, + StringBuilderUnit; + +const + TestString = 'TestString'; + CountOfTestString = 1013; + CountOfTests = 10000; + +procedure TestWithConcat; +var + time: TDateTime; + testIndex, i: Cardinal; + s: string; +begin + WriteLN('Now testing concat...'); + time := Now; + for testIndex := 1 to CountOfTests do + begin + s := ''; + for i := 1 to CountOfTestString do + s := s + TestString; + end; + time := Now - time; + WriteLN(FormatDateTime('hh:nn:ss.zzz', time)); +end; + +{ $Define EnableIntegrityCheck} + +procedure TestWithBuilder; +var + time: TDateTime; + testIndex, i: Cardinal; + resultValid, allValid: Boolean; + builder: TStringBuilder; + s: string; +begin + WriteLN('Now testing concat...'); + time := Now; + allValid := True; + for testIndex := 1 to CountOfTests do + begin + builder := TStringBuilder.Create; + for i := 1 to CountOfTestString do + builder.Add(TestString); + s := builder.ToString; + builder.Free; + // integrity check below: + {$IfDef EnableIntegrityCheck} + resultValid := True; + for i := 1 to Length(s) do + if s[i] <> TestString[(i - 1) mod Length(TestString) + 1] then + resultValid := False; + allValid := allValid and resultValid; + {$EndIf} + end; + time := Now - time; + {$IfDef EnableIntegrityCheck} + WriteLN('All strings are valid: ', allValid); + {$EndIf} + WriteLN(FormatDateTime('hh:nn:ss.zzz', time)); +end; + +begin + TestWithConcat; + TestWithBuilder; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/README.md b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/README.md new file mode 100644 index 000000000..c7e5ed471 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/README.md @@ -0,0 +1,2 @@ +# generics.collections +FreePascal Generics.Collections library (TList, TDictionary, THashMap and more...) diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArrayDouble/TArrayProjectDouble.lpi b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArrayDouble/TArrayProjectDouble.lpi new file mode 100644 index 000000000..44825e2b3 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArrayDouble/TArrayProjectDouble.lpi @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="TArrayProjectDouble"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <Units Count="1"> + <Unit0> + <Filename Value="TArrayProjectDouble.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="TArrayProjectDouble"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="TArrayProjectDouble"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArrayDouble/TArrayProjectDouble.lpr b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArrayDouble/TArrayProjectDouble.lpr new file mode 100644 index 000000000..8ed5c2bf4 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArrayDouble/TArrayProjectDouble.lpr @@ -0,0 +1,91 @@ +// Generic types for FreeSparta.com and FreePascal! +// Original version by keeper89.blogspot.com, 2011 +// FPC version by Maciej Izak (hnb), 2014 + +program TArrayProjectDouble; + +{$MODE DELPHI} +{$APPTYPE CONSOLE} + +uses + SysUtils, Math, Types, Generics.Collections, Generics.Defaults; + +type + TDoubleIntegerArray = array of TIntegerDynArray; + +procedure PrintMatrix(A: TDoubleIntegerArray); +var + i, j: Integer; +begin + for i := Low(A) to High(A) do + begin + for j := Low(A[0]) to High(A[0]) do + Write(A[i, j]: 3, ' '); + Writeln; + end; + Writeln; Writeln; +end; + +function CustomCompare_1(constref Left, Right: TIntegerDynArray): Integer; +begin + Result := TCompare.Integer(Right[0], Left[0]); +end; + +function CustomCompare_2(constref Left, Right: TIntegerDynArray): Integer; +var + i: Integer; +begin + i := 0; + repeat + Result := TCompare.Integer(Right[i], Left[i]); + Inc(i); + until ((Result <> 0) or (i = Length(Left))); +end; + +var + A: TDoubleIntegerArray; + FoundIndex: Integer; + i, j: Integer; + +begin + WriteLn('Working with TArray - a two-dimensional integer array'); + WriteLn; + + // Fill integer array with random numbers [1 .. 50] + SetLength(A, 4, 7); + Randomize; + for i := Low(A) to High(A) do + for j := Low(A[0]) to High(A[0]) do + A[i, j] := Math.RandomRange(1, 50); + + // Equate some of the elements for further "cascade" sorting + A[1, 0] := A[0, 0]; + A[2, 0] := A[0, 0]; + A[1, 1] := A[0, 1]; + + // Print out what happened + Writeln('The original array:'); + PrintMatrix(A); + + // ! FPC don't support anonymous methods yet + //TArray.Sort<TIntegerDynArray>(A, TComparer<TIntegerDynArray>.Construct( + // function (const Left, Right: TIntegerDynArray): Integer + // begin + // Result := Right[0] - Left[0]; + // end)); + // Sort descending 1st column, with cutom comparer_1 + TArrayHelper<TIntegerDynArray>.Sort(A, TComparer<TIntegerDynArray>.Construct( + CustomCompare_1)); + Writeln('Descending in column 1:'); + PrintMatrix(A); + + // Sort descending 1st column "cascade" - + // If the line items are equal, compare neighboring + TArrayHelper<TIntegerDynArray>.Sort(A, TComparer<TIntegerDynArray>.Construct( + CustomCompare_2)); + Writeln('Cascade sorting, starting from the 1st column:'); + PrintMatrix(A); + + Readln; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArraySingle/TArrayProjectSingle.lpi b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArraySingle/TArrayProjectSingle.lpi new file mode 100644 index 000000000..0793ea1d1 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArraySingle/TArrayProjectSingle.lpi @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="TArrayProjectSingle"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <Units Count="1"> + <Unit0> + <Filename Value="TArrayProjectSingle.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="TArrayProjectSingle"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="TArrayProjectSingle"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + </SyntaxOptions> + </Parsing> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArraySingle/TArrayProjectSingle.lpr b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArraySingle/TArrayProjectSingle.lpr new file mode 100644 index 000000000..49bec2cfc --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TArraySingle/TArrayProjectSingle.lpr @@ -0,0 +1,111 @@ +// Generic types for FreeSparta.com and FreePascal! +// Original version by keeper89.blogspot.com, 2011 +// FPC version by Maciej Izak (hnb), 2014 + +program TArrayProjectSingle; + +{$MODE DELPHI} +{$APPTYPE CONSOLE} + +uses + SysUtils, Math, Types, Generics.Collections, Generics.Defaults; + +function CompareIntReverse(constref Left, Right: Integer): Integer; +begin + Result := TCompare.Integer(Right, Left); +end; + +type + TForCompare = class + public + function CompareIntReverseMethod(constref Left, Right: Integer): Integer; + end; + +function TForCompare.CompareIntReverseMethod(constref Left, Right: Integer): Integer; +begin + Result := TCompare.Integer(Right, Left); +end; + +procedure PrintMatrix(A: TIntegerDynArray); +var + item: Integer; +begin + for item in A do + Write(item, ' '); + Writeln; Writeln; +end; + +var + A: TIntegerDynArray; + FoundIndex: PtrInt; + ForCompareObj: TForCompare; +begin + WriteLn('Working with TArray - one-dimensional integer array'); + WriteLn; + + // Fill a one-dimensional array of integers by random numbers [1 .. 10] + A := TIntegerDynArray.Create(1, 6, 3, 2, 9); + + // Print out what happened + Writeln('The original array:'); + PrintMatrix(A); + + // Sort ascending without comparator + TArrayHelper<Integer>.Sort(A); + Writeln('Ascending Sort without parameters:'); + PrintMatrix(A); + + // ! FPC don't support anonymous methods yet + // Sort descending, the comparator is constructed + // using an anonymous method + //TArray.Sort<Integer>(A, TComparer<Integer>.Construct( + // function (const Left, Right: Integer): Integer + // begin + // Result := Math.CompareValue(Right, Left) + // end)); + + // Sort descending, the comparator is constructed + // using an method + TArrayHelper<Integer>.Sort(A, TComparer<Integer>.Construct( + ForCompareObj.CompareIntReverseMethod)); + Writeln('Descending by TComparer<Integer>.Construct(ForCompareObj.Method):'); + PrintMatrix(A); + + // Again sort ascending by using defaul + TArrayHelper<Integer>.Sort(A, TComparer<Integer>.Default); + Writeln('Ascending by TComparer<Integer>.Default:'); + PrintMatrix(A); + + // Again descending using own comparator function + TArrayHelper<Integer>.Sort(A, TComparer<Integer>.Construct(CompareIntReverse)); + Writeln('Descending by TComparer<Integer>.Construct(CompareIntReverse):'); + PrintMatrix(A); + + // Searches for a nonexistent element + Writeln('BinarySearch nonexistent element'); + if TArrayHelper<Integer>.BinarySearch(A, 5, FoundIndex) then + Writeln('5 is found, its index ', FoundIndex) + else + Writeln('5 not found!'); + Writeln; + + // Search for an existing item with default comparer + Writeln('BinarySearch for an existing item '); + if TArrayHelper<Integer>.BinarySearch(A, 6, FoundIndex) then + Writeln('6 is found, its index ', FoundIndex) + else + Writeln('6 not found!'); + Writeln; + + // Search for an existing item with custom comparer + Writeln('BinarySearch for an existing item with custom comparer'); + if TArrayHelper<Integer>.BinarySearch(A, 6, FoundIndex, + TComparer<Integer>.Construct(CompareIntReverse)) then + Writeln('6 is found, its index ', FoundIndex) + else + Writeln('6 not found!'); + Writeln; + + Readln; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TComparer/TComparerProject.lpi b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TComparer/TComparerProject.lpi new file mode 100644 index 000000000..fe598a93f --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TComparer/TComparerProject.lpi @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="TComparerProject"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <Units Count="1"> + <Unit0> + <Filename Value="TComparerProject.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="TComparerProject"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="TComparerProject"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TComparer/TComparerProject.lpr b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TComparer/TComparerProject.lpr new file mode 100644 index 000000000..b7c12823a --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TComparer/TComparerProject.lpr @@ -0,0 +1,124 @@ +// Generic types for FreeSparta.com and FreePascal! +// by Maciej Izak (hnb), 2014 + +program TComparerProject; + +{$MODE DELPHI} +{$APPTYPE CONSOLE} + +uses + SysUtils, Generics.Collections, Generics.Defaults; + +type + + { TCustomer } + + TCustomer = record + private + FName: string; + FMoney: Currency; + public + constructor Create(const Name: string; Money: Currency); + property Name: string read FName write FName; + property Money: Currency read FMoney write FMoney; + function ToString: string; + end; + + TCustomerComparer = class(TComparer<TCustomer>) + function Compare(constref Left, Right: TCustomer): Integer; override; + end; + +{ TCustomer } + +constructor TCustomer.Create(const Name: string; Money: Currency); +begin + FName := Name; + FMoney := Money; +end; + +function TCustomer.ToString: string; +begin + Result := Format('Name: %s >>> Money: %m', [Name, Money]); +end; + +// Ascending +function TCustomerComparer.Compare(constref Left, Right: TCustomer): Integer; +begin + Result := TCompare.&String(Left.Name, Right.Name); + if Result = 0 then + Result := TCompare.Currency(Left.Money, Right.Money); +end; + +// Descending +function CustomerCompare(constref Left, Right: TCustomer): Integer; +begin + Result := TCompare.&String(Right.Name, Left.Name); + if Result = 0 then + Result := TCompare.Currency(Right.Money, Left.Money); +end; + +var + CustomersArray: TArray<TCustomer>; + CustomersList: TList<TCustomer>; + Comparer: TCustomerComparer; + Customer: TCustomer; +begin + CustomersArray := TArray<TCustomer>.Create( + TCustomer.Create('Derp', 2000), + TCustomer.Create('Sheikh', 2000000000), + TCustomer.Create('Derp', 1000), + TCustomer.Create('Bill Gates', 1000000000)); + + Comparer := TCustomerComparer.Create; + Comparer._AddRef; + + // create TList with custom comparer + CustomersList := TList<TCustomer>.Create(Comparer); + CustomersList.AddRange(CustomersArray); + + WriteLn('CustomersList before sort:'); + for Customer in CustomersList do + WriteLn(Customer.ToString); + WriteLn; + + // default sort + CustomersList.Sort; // will use TCustomerComparer (passed in the constructor) + WriteLn('CustomersList after ascending sort (default with interface from constructor):'); + for Customer in CustomersList do + WriteLn(Customer.ToString); + WriteLn; + + // construct with simple function + CustomersList.Sort(TComparer<TCustomer>.Construct(CustomerCompare)); + WriteLn('CustomersList after descending sort (by using construct with function)'); + WriteLn('CustomersList.Sort(TComparer<TCustomer>.Construct(CustomerCompare)):'); + for Customer in CustomersList do + WriteLn(Customer.ToString); + WriteLn; + + // construct with method + CustomersList.Sort(TComparer<TCustomer>.Construct(Comparer.Compare)); + WriteLn('CustomersList after ascending sort (by using construct with method)'); + WriteLn('CustomersList.Sort(TComparer<TCustomer>.Construct(Comparer.Compare)):'); + for Customer in CustomersList do + WriteLn(Customer.ToString); + WriteLn; + + WriteLn('CustomersArray before sort:'); + for Customer in CustomersArray do + WriteLn(Customer.ToString); + WriteLn; + + // sort with interface + TArrayHelper<TCustomer>.Sort(CustomersArray, TCustomerComparer.Create); + WriteLn('CustomersArray after ascending sort (by using interfese - no construct)'); + WriteLn('TArrayHelper<TCustomer>.Sort(CustomersArray, TCustomerComparer.Create):'); + for Customer in CustomersArray do + WriteLn(Customer.ToString); + WriteLn; + + CustomersList.Free; + Comparer._Release; + ReadLn; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMap/THashMapProject.lpi b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMap/THashMapProject.lpi new file mode 100644 index 000000000..87cfc4525 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMap/THashMapProject.lpi @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="THashMapProject"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <Units Count="1"> + <Unit0> + <Filename Value="THashMapProject.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="THashMapProject"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="THashMapProject"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Parsing> + <SyntaxOptions> + <SyntaxMode Value="Delphi"/> + </SyntaxOptions> + </Parsing> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMap/THashMapProject.lpr b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMap/THashMapProject.lpr new file mode 100644 index 000000000..d9598cab9 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMap/THashMapProject.lpr @@ -0,0 +1,218 @@ +// Generic types for FreeSparta.com and FreePascal! +// Original version by keeper89.blogspot.com, 2011 +// FPC version by Maciej Izak (hnb), 2014 + +program THashMapProject; + +{$MODE DELPHI} +{$APPTYPE CONSOLE} + +uses + SysUtils, Generics.Collections, Generics.Defaults; + +type + TSubscriberInfo = record + Name, SName: string; + class function Create(const Name, SName: string): TSubscriberInfo; static; + function ToString: string; + end; + + // Class containing handlers add / remove items in the dictionary + THashMapEventsHandler = class + public + class procedure OnKeyNotify(Sender: TObject; constref Item: string; + Action: TCollectionNotification); + class procedure OnValueNotify(Sender: TObject; constref Item: TSubscriberInfo; + Action: TCollectionNotification); + end; + +class function TSubscriberInfo.Create(const Name, + SName: string): TSubscriberInfo; +begin + Result.Name := Name; + Result.SName := SName; +end; + +function TSubscriberInfo.ToString: string; +begin + Result := Format('%s %s', [Name, SName]); +end; + +// Function to generate the dictionary contents into a string +function PrintTelephoneDirectory( + TelephoneDirectory: THashMap<string, TSubscriberInfo>): string; +var + PhoneNumber: string; +begin + Result := Format('Content directory (%d):', [TelephoneDirectory.Count]); + + for PhoneNumber in TelephoneDirectory.Keys do + Result := Result + Format(LineEnding + '%s: %s', + [PhoneNumber, TelephoneDirectory[PhoneNumber].ToString]); +end; + +// Handlers add / remove items dictionary +class procedure THashMapEventsHandler.OnKeyNotify(Sender: TObject; + constref Item: string; Action: TCollectionNotification); +begin + case Action of + cnAdded: + Writeln(Format('OnKeyNotify! Phone %s added!', [Item])); + cnRemoved: + Writeln(Format('OnKeyNotify! Number %s deleted!', [Item])); + end; +end; + +class procedure THashMapEventsHandler.OnValueNotify(Sender: TObject; + constref Item: TSubscriberInfo; Action: TCollectionNotification); +begin + case Action of + cnAdded: + Writeln(Format('OnValueNotify! Subscriber %s added!', [Item.ToString])); + cnRemoved: + Writeln(Format('OnValueNotify! Subscriber %s deleted!', [Item.ToString])); + end; +end; + +function CustomCompare(constref Left, Right: TPair<string, TSubscriberInfo>): Integer; +begin + // Comparable full first names, and then phones if necessary + Result := TCompare.&String(Left.Value.ToString, Right.Value.ToString); + if Result = 0 then + Result := TCompare.&String(Left.Key, Right.Key); +end; + +var + // Declare the "dictionary" + // key is the telephone number which will be possible + // to determine information about the owner + TelephoneDirectory: THashMap<string, TSubscriberInfo>; + TTelephoneArray: array of TPair<string, TSubscriberInfo>; + TTelephoneArrayItem: TPair<string, TSubscriberInfo>; + PhoneNumber: string; + Subscriber: TSubscriberInfo; +begin + WriteLn('Working with THashMap - phonebook'); + WriteLn; + + // create a directory + // Constructor has several overloaded options which will + // enable the capacity of the container, a comparator for values + // or the initial data - we use the easiest option + TelephoneDirectory := THashMap<string, TSubscriberInfo>.Create; + + // --------------------------------------------------- + // 1) Adding items to dictionary + + // Add new users to the phonebook + TelephoneDirectory.Add('9201111111', TSubscriberInfo.Create('Arnold', 'Schwarzenegger')); + TelephoneDirectory.Add('9202222222', TSubscriberInfo.Create('Jessica', 'Alba')); + TelephoneDirectory.Add('9203333333', TSubscriberInfo.Create('Brad', 'Pitt')); + TelephoneDirectory.Add('9204444444', TSubscriberInfo.Create('Brad', 'Pitt')); + TelephoneDirectory.Add('9205555555', TSubscriberInfo.Create('Sandra', 'Bullock')); + // Adding a new subscriber if number already exist + TelephoneDirectory.AddOrSetValue('9204444444', + TSubscriberInfo.Create('Angelina', 'Jolie')); + // Print list + Writeln(PrintTelephoneDirectory(TelephoneDirectory)); + + // --------------------------------------------------- + // 2) Working with the elements + + // Set the "capacity" of the dictionary according to the current number of elements + TelephoneDirectory.TrimExcess; + // Is there a key? - ContainsKey + if TelephoneDirectory.ContainsKey('9205555555') then + Writeln('Phone 9205555555 registered!'); + // Is there a subscriber? - ContainsValue + Subscriber := TSubscriberInfo.Create('Sandra', 'Bullock'); + if TelephoneDirectory.ContainsValue(Subscriber) then + Writeln(Format('%s is in the directory!', [Subscriber.ToString])); + // Try to get information via telephone. TryGetValue + if TelephoneDirectory.TryGetValue('9204444444', Subscriber) then + Writeln(Format('Number 9204444444 belongs to %s', [Subscriber.ToString])); + // Directly access by phone number + Writeln(Format('Phone 9201111111 subscribers: %s', [TelephoneDirectory['9201111111'].ToString])); + // Number of people in the directory + Writeln(Format('Total subscribers in the directory: %d', [TelephoneDirectory.Count])); + + // --------------------------------------------------- + // 3) Delete items + + // Schwarzenegger now will not be listed + TelephoneDirectory.Remove('9201111111'); + // Completely clear the list + TelephoneDirectory.Clear; + + // --------------------------------------------------- + // 4) Events add / remove values + // + // Events OnKeyNotify OnValueNotify are designed for "tracking" + // for adding / removing keys and values ​​respectively + TelephoneDirectory.OnKeyNotify := THashMapEventsHandler.OnKeyNotify; + TelephoneDirectory.OnValueNotify := THashMapEventsHandler.OnValueNotify; + + Writeln; + // Try events + TelephoneDirectory.Add('9201111111', TSubscriberInfo.Create('Arnold', 'Schwarzenegger')); + TelephoneDirectory.Add('9202222222', TSubscriberInfo.Create('Jessica', 'Alba')); + TelephoneDirectory['9202222222'] := TSubscriberInfo.Create('Monica', 'Bellucci'); + TelephoneDirectory.Clear; + WriteLn; + + TelephoneDirectory.Add('9201111111', TSubscriberInfo.Create('Monica', 'Bellucci')); + TelephoneDirectory.Add('9202222222', TSubscriberInfo.Create('Sylvester', 'Stallone')); + TelephoneDirectory.Add('9203333333', TSubscriberInfo.Create('Bruce', 'Willis')); + WriteLn; + + // Show keys (phones) + Writeln('Keys (phones):'); + for PhoneNumber in TelephoneDirectory.Keys do + Writeln(PhoneNumber); + Writeln; + + // Show values ​​(subscribers) + Writeln('Values (subscribers):'); + for Subscriber in TelephoneDirectory.Values do + Writeln(Subscriber.ToString); + Writeln; + + // All together now + Writeln('Subscribers list with phones:'); + for PhoneNumber in TelephoneDirectory.Keys do + Writeln(Format('%s: %s', + [PhoneNumber, TelephoneDirectory[PhoneNumber].ToString])); + Writeln; + + // In addition, we can "export" from the dictionary + // to TArray + // Sort the resulting array and display + TTelephoneArray := TelephoneDirectory.ToArray; + + // partial specializations not allowed + // same for anonymous methods + //TArray.Sort<TPair<string, TSubscriberInfo>>( + // TTelephoneArray, TComparer<TPair<string, TSubscriberInfo>>.Construct( + // function (const Left, Right: TPair<string, TSubscriberInfo>): Integer + // begin + // // Comparable full first names, and then phones if necessary + // Result := CompareStr(Left.Value.ToString, Right.Value.ToString); + // if Result = 0 then + // Result := CompareStr(Left.Key, Right.Key); + // end)); + + TArrayHelper<TelephoneDirectory.TDictionaryPair>.Sort( + TTelephoneArray, TComparer<TelephoneDirectory.TDictionaryPair>.Construct( + CustomCompare)); + // Print + Writeln('Sorted list of subscribers into TArray (by name, and eventually by phone):'); + for TTelephoneArrayItem in TTelephoneArray do + Writeln(Format('%s: %s', + [TTelephoneArrayItem.Value.ToString, TTelephoneArrayItem.Key])); + + Writeln; + FreeAndNil(TelephoneDirectory); + + Readln; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapCaseInsensitive/THashMapCaseInsensitive.lpi b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapCaseInsensitive/THashMapCaseInsensitive.lpi new file mode 100644 index 000000000..097b7714e --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapCaseInsensitive/THashMapCaseInsensitive.lpi @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="THashMapCaseInsensitive"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <Units Count="1"> + <Unit0> + <Filename Value="THashMapCaseInsensitive.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="THashMapCaseInsensitive"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="THashMapCaseInsensitive"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapCaseInsensitive/THashMapCaseInsensitive.lpr b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapCaseInsensitive/THashMapCaseInsensitive.lpr new file mode 100644 index 000000000..377bd69c1 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapCaseInsensitive/THashMapCaseInsensitive.lpr @@ -0,0 +1,55 @@ +// Generic types for FreeSparta.com and FreePascal! +// by Maciej Izak (hnb), 2014 + +program THashMapCaseInsensitive; + +{$MODE DELPHI} +{$APPTYPE CONSOLE} + +uses + Generics.Collections, Generics.Defaults; + +var + StringMap: THashMap<String, TEmptyRecord>; + AnsiStringMap: THashMap<AnsiString, TEmptyRecord>; + UnicodeStringMap: THashMap<UnicodeString, TEmptyRecord>; + AdvancedHashMapWithBigLoadFactor: TCuckooD6<RawByteString, TEmptyRecord>; + k: String; +begin + WriteLn('Working with case insensitive THashMap'); + WriteLn; + // example constructors for different string types + StringMap := THashMap<String, TEmptyRecord>.Create(TIStringComparer.Ordinal); + StringMap.Free; + AnsiStringMap := THashMap<AnsiString, TEmptyRecord>.Create(TIAnsiStringComparer.Ordinal); + AnsiStringMap.Free; + UnicodeStringMap := THashMap<UnicodeString, TEmptyRecord>.Create(TIUnicodeStringComparer.Ordinal); + UnicodeStringMap.Free; + + // standard TI*Comparer is dedicated for MAX_HASHLIST_COUNT = 4 and lower. For example DArrayCuckoo where D = 6 + // we need to create extra specialized TGIStringComparer type + AdvancedHashMapWithBigLoadFactor := TCuckooD6<RawByteString, TEmptyRecord>.Create( + TGIStringComparer<RawByteString, TDelphiSixfoldHashFactory>.Ordinal); + AdvancedHashMapWithBigLoadFactor.Free; + + // ok lets start + // another way to create case insensitive hash map + StringMap := THashMap<String, TEmptyRecord>.Create(TGIStringComparer<String>.Ordinal); + + WriteLn('Add Cat and Dog'); + StringMap.Add('Cat', EmptyRecord); + StringMap.Add('Dog', EmptyRecord); + + // + WriteLn('Contains CAT = ', StringMap.ContainsKey('CAT')); + WriteLn('Contains dOG = ', StringMap.ContainsKey('dOG')); + WriteLn('Contains Fox = ', StringMap.ContainsKey('Fox')); + + WriteLn('Enumerate all keys :'); + for k in StringMap.Keys do + WriteLn(' > ', k); + + ReadLn; + StringMap.Free; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapExtendedEqualityComparer/THashMapExtendedEqualityComparer.lpi b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapExtendedEqualityComparer/THashMapExtendedEqualityComparer.lpi new file mode 100644 index 000000000..0a8edbe0b --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapExtendedEqualityComparer/THashMapExtendedEqualityComparer.lpi @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="THashMapExtendedEqualityComparer"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <Units Count="1"> + <Unit0> + <Filename Value="THashMapExtendedEqualityComparer.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="THashMapExtendedEqualityComparer"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="THashMapExtendedEqualityComparer"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapExtendedEqualityComparer/THashMapExtendedEqualityComparer.lpr b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapExtendedEqualityComparer/THashMapExtendedEqualityComparer.lpr new file mode 100644 index 000000000..d3c9116c7 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/THashMapExtendedEqualityComparer/THashMapExtendedEqualityComparer.lpr @@ -0,0 +1,108 @@ +// Generic types for FreeSparta.com and FreePascal! +// by Maciej Izak (hnb), 2014 + +program THashMapExtendedEqualityComparer; + +{$MODE DELPHI} +{$APPTYPE CONSOLE} + +uses + SysUtils, Generics.Collections, Generics.Defaults; + +type + + { TTaxPayer } + + TTaxPayer = record + TaxID: Integer; + Name: string; + + constructor Create(ATaxID: Integer; const AName: string); + function ToString: string; + end; + +constructor TTaxPayer.Create(ATaxID: Integer; const AName: string); +begin + TaxID := ATaxID; + Name := AName; +end; + +function TTaxPayer.ToString: string; +begin + Result := Format('TaxID = %-10d Name = %-17s', [TaxID, Name]); +end; + +function EqualityComparison(constref ALeft, ARight: TTaxPayer): Boolean; +begin + Result := ALeft.TaxID = ARight.TaxID; +end; + +procedure ExtendedHasher(constref AValue: TTaxPayer; AHashList: PUInt32); +begin + // don't work with TCuckooD6 map because default TCuckooD6 needs TDelphiSixfoldHashFactory + // and TDefaultHashFactory = TDelphiQuadrupleHashFactory + // (TDelphiQuadrupleHashFactory is compatible with TDelphiDoubleHashFactory and TDelphiHashFactory) + TDefaultHashFactory.GetHashList(@AValue.TaxID, SizeOf(Integer), AHashList); +end; + +var + map: THashMap<TTaxPayer, string>; // THashMap = TCuckooD4 + LTaxPayer: TTaxPayer; + LSansa: TTaxPayer; + LPair: TPair<TTaxPayer, string>; +begin + WriteLn('program of tax office - ExtendedEqualityComparer for THashMap'); + WriteLn; + + // to identify the taxpayer need only nip + map := THashMap<TTaxPayer, string>.Create( + TExtendedEqualityComparer<TTaxPayer>.Construct(EqualityComparison, ExtendedHasher)); + + map.Add(TTaxPayer.Create(1234567890, 'Joffrey Baratheon'), 'guilty'); + map.Add(TTaxPayer.Create(90, 'Little Finger'), 'swindler'); + map.Add(TTaxPayer.Create(667, 'John Snow'), 'delinquent tax'); + + // useless in this place but we can convert Keys to TArray<TKey> :) + WriteLn(Format('All taxpayers (count = %d)', [Length(map.Keys.ToArray)])); + for LTaxPayer in map.Keys do + WriteLn(' > ', LTaxPayer.ToString); + + LSansa := TTaxPayer.Create(667, 'Sansa Stark'); + + // exist because custom EqualityComparison and ExtendedHasher + WriteLn; + WriteLn(LSansa.Name, ' exist in map = ', map.ContainsKey(LSansa)); + WriteLn; + + // + WriteLn('All taxpayers'); + for LPair in map do + WriteLn(' > ', LPair.Key.ToString, ' is ', LPair.Value); + + // Add or set sansa? :) + WriteLn; + WriteLn(Format('AddOrSet(%s, ''innocent'')', [LSansa.ToString])); + map.AddOrSetValue(LSansa, 'innocent'); + WriteLn; + + // + WriteLn('All taxpayers'); + for LPair in map do + WriteLn(' > ', LPair.Key.ToString, ' is ', LPair.Value); + + // Add or set sansa? :) + WriteLn; + LSansa.TaxID := 668; + WriteLn(Format('AddOrSet(%s, ''innocent'')', [LSansa.ToString])); + map.AddOrSetValue(LSansa, 'innocent'); + WriteLn; + + // + WriteLn('All taxpayers'); + for LPair in map do + WriteLn(' > ', LPair.Key.ToString, ' is ', LPair.Value); + + ReadLn; + map.Free; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TObjectList/TObjectListProject.lpi b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TObjectList/TObjectListProject.lpi new file mode 100644 index 000000000..af14cd9a1 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TObjectList/TObjectListProject.lpi @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="TObjectListProject"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <Units Count="1"> + <Unit0> + <Filename Value="TObjectListProject.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="TObjectListProject"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="TObjectListProject"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TObjectList/TObjectListProject.lpr b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TObjectList/TObjectListProject.lpr new file mode 100644 index 000000000..179d88595 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TObjectList/TObjectListProject.lpr @@ -0,0 +1,194 @@ +// Generic types for FreeSparta.com and FreePascal! +// Original version by keeper89.blogspot.com, 2011 +// FPC version by Maciej Izak (hnb), 2014 + +program TObjectListProject; + +{$MODE DELPHI} +{$APPTYPE CONSOLE} + +uses + SysUtils, Generics.Collections, Generics.Defaults, DateUtils; + +type + TPlayer = class + public + Name, Team: string; + BirthDay: TDateTime; + NTeamGoals: Byte; // Number of goals for the national team + constructor Create(const Name: string; BirthDay: TDateTime; + const Team: string; NTeamGoals: Byte = 0); + function ToString: string; + end; + + // Class containing handlers add / remove list items + TListEventsHandler = class + public + class procedure OnListChanged(Sender: TObject; constref Item: TPlayer; + Action: TCollectionNotification); + end; + + +constructor TPlayer.Create(const Name: string; BirthDay: TDateTime; + const Team: string; NTeamGoals: Byte); +begin + Self.Name := Name; + Self.BirthDay := BirthDay; + Self.Team := Team; + Self.NTeamGoals := NTeamGoals; +end; + +function TPlayer.ToString: string; +begin + Result := Format('%s - Age: %d Team: %s Goals: %d', + [Name, + DateUtils.YearsBetween(Date, BirthDay), + Team, NTeamGoals]) +end; + +// Function sort descending goals for the national team +function ComparePlayersByGoalsDecs(constref Player1, Player2: TPlayer): Integer; +begin + Result := TCompare.UInt8(Player2.NTeamGoals, Player1.NTeamGoals); +end; + +class procedure TListEventsHandler.OnListChanged(Sender: TObject; constref Item: TPlayer; + Action: TCollectionNotification); +var + Mes: string; +begin + // Unlike TDictionary we added Action = cnExtracted + case Action of + cnAdded: + Mes := 'added to the list!'; + cnRemoved: + Mes := 'removed from the list!'; + cnExtracted: + Mes := 'extracted from the list!'; + end; + Writeln(Format('Football player %s %s ', [Item.ToString, Mes])); +end; + +var + // Declare TObjectList as storage for TPlayer + PlayersList: TObjectList<TPlayer>; + Player: TPlayer; + FoundIndex: PtrInt; +begin + WriteLn('Working with TObjectList - football manager'); + WriteLn; + + PlayersList := TObjectList<TPlayer>.Create; + + // --------------------------------------------------- + // 1) Adding items + + PlayersList.Add( + TPlayer.Create('Zinedine Zidane', EncodeDate(1972, 06, 23), 'France', 31)); + PlayersList.Add( + TPlayer.Create('Raul', EncodeDate(1977, 06, 27), 'Spain', 44)); + PlayersList.Add( + TPlayer.Create('Ronaldo', EncodeDate(1976, 09, 22), 'Brazil', 62)); + // Adding the specified position + PlayersList.Insert(0, + TPlayer.Create('Luis Figo', EncodeDate(1972, 11, 4), 'Portugal', 33)); + // Add a few players through InsertRange (AddRange works similarly) + PlayersList.InsertRange(0, + [TPlayer.Create('David Beckham', EncodeDate(1975, 05, 2), 'England', 17), + TPlayer.Create('Alessandro Del Piero', EncodeDate(1974, 11, 9), 'Italy ', 27), + TPlayer.Create('Raul', EncodeDate(1977, 06, 27), 'Spain', 44)]); + Player := TPlayer.Create('Raul', EncodeDate(1977, 06, 27), 'Spain', 44); + PlayersList.Add(Player); + + + // --------------------------------------------------- + // 2) Access and check the items + + // Is there a player in the list - Contains + if PlayersList.Contains(Player) then + Writeln('Raul is in the list!'); + // Player index and count of items in the list + Writeln(Format('Raul is %d-th on the list of %d players.', + [PlayersList.IndexOf(Player) + 1, PlayersList.Count])); + // Index access + Writeln(Format('1st in the list: %s', [PlayersList[0].ToString])); + // The first player + Writeln(Format('1st in the list: %s', [PlayersList.First.ToString])); + // The last player + Writeln(Format('Last in the list: %s', [PlayersList.Last.ToString])); + // "Reverse" elements + PlayersList.Reverse; + Writeln('List items have been "reversed"'); + Writeln; + + + // --------------------------------------------------- + // 3) Moving and removing items + + // Changing places players in the list + PlayersList.Exchange(0, 1); + // Move back 1 player + PlayersList.Move(1, 0); + + // Removes the element at index + PlayersList.Delete(5); + // Or a number of elements starting at index + PlayersList.DeleteRange(5, 2); + // Remove the item from the list, if the item + // exists returns its index in the list + Writeln(Format('Removed %d-st player', [PlayersList.Remove(Player) + 1])); + + // Extract and return the item, if there is no Player in the list then + // Extract will return = nil, (anyway Raul is already removed via Remove) + Player := PlayersList.Extract(Player); + if Assigned(Player) then + Writeln(Format('Extracted: %s', [Player.ToString])); + + // Clear the list completely + PlayersList.Clear; + Writeln; + + // --------------------------------------------------- + // 4) Event OnNotify, sorting and searching + + PlayersList.OnNotify := TListEventsHandler.OnListChanged; + + PlayersList.Add( + TPlayer.Create('Zinedine Zidane', EncodeDate(1972, 06, 23), 'France', 31)); + PlayersList.Add( + TPlayer.Create('Raul', EncodeDate(1977, 06, 27), 'Spain', 44)); + PlayersList.Add( + TPlayer.Create('Ronaldo', EncodeDate(1976, 09, 22), 'Brazil', 62)); + PlayersList.AddRange( + [TPlayer.Create('David Beckham', EncodeDate(1975, 05, 2), 'England', 17), + TPlayer.Create('Alessandro Del Piero', EncodeDate(1974, 11, 9), 'Italy ', 27), + TPlayer.Create('Raul', EncodeDate(1977, 06, 27), 'Spain', 44)]); + + PlayersList.Remove(PlayersList.Last); + Player := PlayersList.Extract(PlayersList[0]); + + PlayersList.Sort(TComparer<TPlayer>.Construct(ComparePlayersByGoalsDecs)); + Writeln; + Writeln('Sorted list of players:'); + for Player in PlayersList do + Writeln(Player.ToString); + Writeln; + + // Find Ronaldo! + // TArray BinarySearch requires sorted list + // IndexOf does not require sorted list + // but BinarySearch is usually faster + Player := PlayersList[0]; + if PlayersList.BinarySearch(Player, FoundIndex, + TComparer<TPlayer>.Construct(ComparePlayersByGoalsDecs)) then + Writeln(Format('Ronaldo is in the sorted list at position %d', [FoundIndex + 1])); + + Writeln; + + // With the destruction of the list remove all elements + // OnNotify show it + FreeAndNil(PlayersList); + + Readln; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TQueue/TQueueProject.lpi b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TQueue/TQueueProject.lpi new file mode 100644 index 000000000..8d8658b8b --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TQueue/TQueueProject.lpi @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="TQueueProject"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <Units Count="1"> + <Unit0> + <Filename Value="TQueueProject.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="TQueueProject"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="TQueueProject"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TQueue/TQueueProject.lpr b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TQueue/TQueueProject.lpr new file mode 100644 index 000000000..87e51cd10 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TQueue/TQueueProject.lpr @@ -0,0 +1,89 @@ +// Generic types for FreeSparta.com and FreePascal! +// Original version by keeper89.blogspot.com, 2011 +// FPC version by Maciej Izak (hnb), 2014 + +program TQueueProject; + +{$MODE DELPHI} +{$APPTYPE CONSOLE} + +uses + SysUtils, Generics.Collections; + +type + // This is FreeSpaaarta! versions =) + TSpartaVersion = (svFreeSparta, svBasic, svStarter, svProfessional); + + TCustomer = record + strict private + const + SV_NAMES: array [TSpartaVersion] of string = + ('FreeSparta', 'Basic', 'Starter', 'Professional'); + public + var + SpartaVersion: TSpartaVersion; + class function Create(SpartaVersion: TSpartaVersion): TCustomer; static; + function ToString: string; + end; + +class function TCustomer.Create(SpartaVersion: TSpartaVersion): TCustomer; +begin + Result.SpartaVersion := SpartaVersion; +end; + +function TCustomer.ToString: string; +begin + Result := Format('Sparta %s', [SV_NAMES[SpartaVersion]]) +end; + +var + CustomerQueue: TQueue<TCustomer>; + Customer: TCustomer; +begin + WriteLn('Working with TQueue - buy FreeSparta.com'); + WriteLn; + + // "Create" turn in sales + CustomerQueue := TQueue<TCustomer>.Create; + + // Add a few people in the queue + // Enqueue - puts the item in the queue + CustomerQueue.Enqueue(TCustomer.Create(svFreeSparta)); + CustomerQueue.Enqueue(TCustomer.Create(svBasic)); + CustomerQueue.Enqueue(TCustomer.Create(svBasic)); + CustomerQueue.Enqueue(TCustomer.Create(svBasic)); + CustomerQueue.Enqueue(TCustomer.Create(svStarter)); + CustomerQueue.Enqueue(TCustomer.Create(svStarter)); + CustomerQueue.Enqueue(TCustomer.Create(svProfessional)); + CustomerQueue.Enqueue(TCustomer.Create(svProfessional)); + + // Part of customers served + // Dequeue - remove an element from the queue + // btw if TQueue is TObjectQueue also call Free for object + Customer := CustomerQueue.Dequeue; + Writeln(Format('Sold (Dequeue): %s', [Customer.ToString])); + // Extract - similar to Dequeue, but causes in OnNotify + // Action = cnExtracted instead cnRemoved + Customer := CustomerQueue.Extract; + Writeln(Format('Sold (Extract): %s', [Customer.ToString])); + + // For what came next buyer? + // Peek - returns the first element, but does not remove it from the queue + Writeln(Format('Serves customers come for %s', + [CustomerQueue.Peek.ToString])); + + // The remaining buyers + Writeln; + Writeln(Format('Buyers left: %d', [CustomerQueue.Count])); + for Customer in CustomerQueue do + Writeln(Customer.ToString); + + // We serve all + // Clear - clears the queue + CustomerQueue.Clear; + + FreeAndNil(CustomerQueue); + + Readln; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TStack/TStackProject.lpi b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TStack/TStackProject.lpi new file mode 100644 index 000000000..9348d16d0 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TStack/TStackProject.lpi @@ -0,0 +1,73 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="9"/> + <PathDelim Value="\"/> + <General> + <Flags> + <MainUnitHasCreateFormStatements Value="False"/> + <MainUnitHasTitleStatement Value="False"/> + </Flags> + <SessionStorage Value="InProjectDir"/> + <MainUnit Value="0"/> + <Title Value="TStackProject"/> + <UseAppBundle Value="False"/> + <ResourceType Value="res"/> + </General> + <i18n> + <EnableI18N LFM="False"/> + </i18n> + <VersionInfo> + <StringTable ProductVersion=""/> + </VersionInfo> + <BuildModes Count="1"> + <Item1 Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + </PublishOptions> + <RunParams> + <local> + <FormatVersion Value="1"/> + </local> + </RunParams> + <Units Count="1"> + <Unit0> + <Filename Value="TStackProject.lpr"/> + <IsPartOfProject Value="True"/> + <UnitName Value="TStackProject"/> + </Unit0> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <PathDelim Value="\"/> + <Target> + <Filename Value="TStackProject"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <OtherUnitFiles Value="..\.."/> + <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Other> + <CompilerMessages> + <MsgFileName Value=""/> + </CompilerMessages> + <CompilerPath Value="$(CompPath)"/> + </Other> + </CompilerOptions> + <Debugging> + <Exceptions Count="3"> + <Item1> + <Name Value="EAbort"/> + </Item1> + <Item2> + <Name Value="ECodetoolError"/> + </Item2> + <Item3> + <Name Value="EFOpenError"/> + </Item3> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TStack/TStackProject.lpr b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TStack/TStackProject.lpr new file mode 100644 index 000000000..1a53e1872 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/examples/TStack/TStackProject.lpr @@ -0,0 +1,86 @@ +// Generic types for FreeSparta.com and FreePascal! +// Original version by keeper89.blogspot.com, 2011 +// FPC version by Maciej Izak (hnb), 2014 +program TStackProject; + +{$MODE DELPHI} +{$APPTYPE CONSOLE} + +uses + SysUtils, + Windows, + Generics.Collections; + +type + // We will cook pancakes, put them on a plate and take the last + TPancakeType = (ptMeat, ptCherry, ptCurds); + + TPancake = record + strict private + const + PANCAKE_TYPE_NAMES: array [TPancakeType] of string = + ('meat', 'cherry', 'curds'); + public + var + PancakeType: TPancakeType; + class function Create(PancakeType: TPancakeType): TPancake; static; + function ToString: string; + end; + +class function TPancake.Create(PancakeType: TPancakeType): TPancake; +begin + Result.PancakeType := PancakeType; +end; + +function TPancake.ToString: string; +begin + Result := Format('Pancake with %s', [PANCAKE_TYPE_NAMES[PancakeType]]) +end; + +var + PancakesPlate: TStack<TPancake>; + Pancake: TPancake; + +begin + WriteLn('Working with TStack - pancakes'); + WriteLn; + + // "Create" a plate of pancakes + PancakesPlate := TStack<TPancake>.Create; + + // Bake some pancakes + // Push - puts items on the stack + PancakesPlate.Push(TPancake.Create(ptMeat)); + PancakesPlate.Push(TPancake.Create(ptCherry)); + PancakesPlate.Push(TPancake.Create(ptCherry)); + PancakesPlate.Push(TPancake.Create(ptCurds)); + PancakesPlate.Push(TPancake.Create(ptMeat)); + + // Eating some pancakes + // Pop - removes an item from the stack + Pancake := PancakesPlate.Pop; + Writeln(Format('Ate a pancake (Pop): %s', [Pancake.ToString])); + // Extract - similar to Pop, but causes in OnNotify + // Action = cnExtracted instead of cnRemoved + Pancake := PancakesPlate.Extract; + Writeln(Format('Ate a pancake (Extract): %s', [Pancake.ToString])); + + // What is the last pancake? + // Peek - returns the last item, but does not remove it from the stack + Writeln(Format('Last pancake: %s', [PancakesPlate.Peek.ToString])); + + // Show the remaining pancakes + Writeln; + Writeln(Format('Total pancakes: %d', [PancakesPlate.Count])); + for Pancake in PancakesPlate do + Writeln(Pancake.ToString); + + // Eat up all + // Clear - clears the stack + PancakesPlate.Clear; + + FreeAndNil(PancakesPlate); + + Readln; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.collections.pas b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.collections.pas new file mode 100644 index 000000000..379a785e1 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.collections.pas @@ -0,0 +1,1263 @@ +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2014 by Maciej Izak (hnb) + member of the Free Sparta development team (http://freesparta.com) + + Copyright(c) 2004-2014 DaThoX + + It contains the Free Pascal generics library + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} + +unit Generics.Collections; + +{$MODE DELPHI}{$H+} +{$MACRO ON} +{$COPERATORS ON} +{$DEFINE CUSTOM_DICTIONARY_CONSTRAINTS := TKey, TValue, THashFactory} +{$DEFINE OPEN_ADDRESSING_CONSTRAINTS := TKey, TValue, THashFactory, TProbeSequence} +{$DEFINE CUCKOO_CONSTRAINTS := TKey, TValue, THashFactory, TCuckooCfg} +{$WARNINGS OFF} +{$HINTS OFF} + +interface + +uses + Classes, SysUtils, Generics.MemoryExpanders, Generics.Defaults, + Generics.Helpers, Generics.Strings; + +{ FPC BUGS related to Generics.* (54 bugs, 19 fixed) + REGRESSION: 26483, 26481 + FIXED REGRESSION: 26480, 26482 + + CRITICAL: 24848(!!!), 24872(!), 25607(!), 26030, 25917, 25918, 25620, 24283, 24254, 24287 (Related to? 24872) + IMPORTANT: 23862(!), 24097, 24285, 24286 (Similar to? 24285), 24098, 24609 (RTL inconsistency), 24534, + 25606, 25614, 26177, 26195 + OTHER: 26484, 24073, 24463, 25593, 25596, 25597, 25602, 26181 (or MYBAD?) + CLOSED BUT IMO STILL TO FIX: 25601(!), 25594 + FIXED: 25610(!), 24064, 24071, 24282, 24458, 24867, 24871, 25604, 25600, 25605, 25598, 25603, 25929, 26176, 26180, + 26193, 24072 + MYBAD: 24963, 25599 +} + +{ LAZARUS BUGS related to Generics.* (7 bugs, 0 fixed) + CRITICAL: 25613 + OTHER: 25595, 25612, 25615, 25617, 25618, 25619 +} + +type + TArray<T> = array of T; // for name TArray<T> conflict with TArray record implementation (bug #26030) + + // bug #24254 workaround + // should be TArray = record class procedure Sort<T>(...) etc. + TCustomArrayHelper<T> = class abstract + private + type + // bug #24282 + TComparerBugHack = TComparer<T>; + protected + // modified QuickSort from classes\lists.inc + class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; const AComparer: IComparer<T>); + virtual; abstract; + public + class procedure Sort(var AValues: array of T); overload; + class procedure Sort(var AValues: array of T; + const AComparer: IComparer<T>); overload; + class procedure Sort(var AValues: array of T; + const AComparer: IComparer<T>; AIndex, ACount: SizeInt); overload; + + class function BinarySearch(constref AValues: array of T; constref AItem: T; + out AFoundIndex: SizeInt; const AComparer: IComparer<T>; + AIndex, ACount: SizeInt): Boolean; virtual; abstract; overload; + class function BinarySearch(constref AValues: array of T; constref AItem: T; + out AFoundIndex: SizeInt; const AComparer: IComparer<T>): Boolean; overload; + class function BinarySearch(constref AValues: array of T; constref AItem: T; + out AFoundIndex: SizeInt): Boolean; overload; + end experimental; // will be renamed to TCustomArray (bug #24254) + + TArrayHelper<T> = class(TCustomArrayHelper<T>) + protected + // modified QuickSort from classes\lists.inc + class procedure QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; const AComparer: IComparer<T>); override; + public + class function BinarySearch(constref AValues: array of T; constref AItem: T; + out AFoundIndex: SizeInt; const AComparer: IComparer<T>; + AIndex, ACount: SizeInt): Boolean; override; overload; + end experimental; // will be renamed to TArray (bug #24254) + + TCollectionNotification = (cnAdded, cnRemoved, cnExtracted); + TCollectionNotifyEvent<T> = procedure(ASender: TObject; constref AItem: T; AAction: TCollectionNotification) + of object; + + { TEnumerator } + + TEnumerator<T> = class abstract + protected + function DoGetCurrent: T; virtual; abstract; + function DoMoveNext: boolean; virtual; abstract; + public + property Current: T read DoGetCurrent; + function MoveNext: boolean; + end; + + { TEnumerable } + + TEnumerable<T> = class abstract + protected + function ToArrayImpl(ACount: SizeInt): TArray<T>; overload; // used by descendants + protected + function DoGetEnumerator: TEnumerator<T>; virtual; abstract; + public + function GetEnumerator: TEnumerator<T>; inline; + function ToArray: TArray<T>; virtual; overload; + end; + + // More info: http://stackoverflow.com/questions/5232198/about-vectors-growth + // TODO: custom memory managers (as constraints) + {$DEFINE CUSTOM_LIST_CAPACITY_INC := Result + Result div 2} // ~approximation to golden ratio: n = n * 1.5 } + // {$DEFINE CUSTOM_LIST_CAPACITY_INC := Result * 2} // standard inc + TCustomList<T> = class abstract(TEnumerable<T>) + protected + type // bug #24282 + TArrayHelperBugHack = TArrayHelper<T>; + private + FOnNotify: TCollectionNotifyEvent<T>; + function GetCapacity: SizeInt; inline; + protected + FItemsLength: SizeInt; + FItems: array of T; + + function PrepareAddingItem: SizeInt; virtual; + function PrepareAddingRange(ACount: SizeInt): SizeInt; virtual; + procedure Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); virtual; + function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; virtual; + procedure SetCapacity(AValue: SizeInt); virtual; abstract; + function GetCount: SizeInt; virtual; + public + function ToArray: TArray<T>; override; final; + + property Count: SizeInt read GetCount; + property Capacity: SizeInt read GetCapacity write SetCapacity; + property OnNotify: TCollectionNotifyEvent<T> read FOnNotify write FOnNotify; + end; + + TCustomListEnumerator<T> = class abstract(TEnumerator< T >) + private + FList: TCustomList<T>; + FIndex: SizeInt; + protected + function DoMoveNext: boolean; override; + function DoGetCurrent: T; override; + function GetCurrent: T; virtual; + public + constructor Create(AList: TCustomList<T>); + end; + + TList<T> = class(TCustomList<T>) + private var + FComparer: IComparer<T>; + protected + // bug #24287 - workaround for generics type name conflict (Identifier not found) + // next bug workaround - for another error related to previous workaround + // change order (method must be declared before TEnumerator declaration) + function DoGetEnumerator: {Generics.Collections.}TEnumerator<T>; override; + public + // with this type declaration i found #24285, #24285 + type + // bug workaround + TEnumerator = class(TCustomListEnumerator<T>); + + function GetEnumerator: TEnumerator; reintroduce; + protected + procedure SetCapacity(AValue: SizeInt); override; + procedure SetCount(AValue: SizeInt); + private + function GetItem(AIndex: SizeInt): T; + procedure SetItem(AIndex: SizeInt; const AValue: T); + public + constructor Create; overload; + constructor Create(const AComparer: IComparer<T>); overload; + constructor Create(ACollection: TEnumerable<T>); overload; + destructor Destroy; override; + + function Add(constref AValue: T): SizeInt; + procedure AddRange(constref AValues: array of T); overload; + procedure AddRange(const AEnumerable: IEnumerable<T>); overload; + procedure AddRange(AEnumerable: TEnumerable<T>); overload; + + procedure Insert(AIndex: SizeInt; constref AValue: T); + procedure InsertRange(AIndex: SizeInt; constref AValues: array of T); overload; + procedure InsertRange(AIndex: SizeInt; const AEnumerable: IEnumerable<T>); overload; + procedure InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerable<T>); overload; + + function Remove(constref AValue: T): SizeInt; + procedure Delete(AIndex: SizeInt); inline; + procedure DeleteRange(AIndex, ACount: SizeInt); + function ExtractIndex(const AIndex: SizeInt): T; overload; + function Extract(constref AValue: T): T; overload; + + procedure Exchange(AIndex1, AIndex2: SizeInt); + procedure Move(AIndex, ANewIndex: SizeInt); + + function First: T; inline; + function Last: T; inline; + + procedure Clear; + + function Contains(constref AValue: T): Boolean; inline; + function IndexOf(constref AValue: T): SizeInt; virtual; + function LastIndexOf(constref AValue: T): SizeInt; virtual; + + procedure Reverse; + + procedure TrimExcess; + + procedure Sort; overload; + procedure Sort(const AComparer: IComparer<T>); overload; + function BinarySearch(constref AItem: T; out AIndex: SizeInt): Boolean; overload; + function BinarySearch(constref AItem: T; out AIndex: SizeInt; const AComparer: IComparer<T>): Boolean; overload; + + property Count: SizeInt read FItemsLength write SetCount; + property Items[Index: SizeInt]: T read GetItem write SetItem; default; + end; + + TQueue<T> = class(TCustomList<T>) + protected + // bug #24287 - workaround for generics type name conflict (Identifier not found) + // next bug workaround - for another error related to previous workaround + // change order (function must be declared before TEnumerator declaration} + function DoGetEnumerator: {Generics.Collections.}TEnumerator<T>; override; + public + type + TEnumerator = class(TCustomListEnumerator<T>) + public + constructor Create(AQueue: TQueue<T>); + end; + + function GetEnumerator: TEnumerator; reintroduce; + private + FLow: SizeInt; + protected + procedure SetCapacity(AValue: SizeInt); override; + function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; override; + function GetCount: SizeInt; override; + public + constructor Create(ACollection: TEnumerable<T>); overload; + destructor Destroy; override; + procedure Enqueue(constref AValue: T); + function Dequeue: T; + function Extract: T; + function Peek: T; + procedure Clear; + procedure TrimExcess; + end; + + TStack<T> = class(TCustomList<T>) + protected + // bug #24287 - workaround for generics type name conflict (Identifier not found) + // next bug workaround - for another error related to previous workaround + // change order (function must be declared before TEnumerator declaration} + function DoGetEnumerator: {Generics.Collections.}TEnumerator<T>; override; + public + type + TEnumerator = class(TCustomListEnumerator<T>); + + function GetEnumerator: TEnumerator; reintroduce; + protected + function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; override; + procedure SetCapacity(AValue: SizeInt); override; + public + constructor Create(ACollection: TEnumerable<T>); overload; + destructor Destroy; override; + procedure Clear; + procedure Push(constref AValue: T); + function Pop: T; inline; + function Peek: T; + function Extract: T; inline; + procedure TrimExcess; + end; + + TObjectList<T: class> = class(TList<T>) + private + FObjectsOwner: Boolean; + protected + procedure Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); override; + public + constructor Create(AOwnsObjects: Boolean = True); overload; + constructor Create(const AComparer: IComparer<T>; AOwnsObjects: Boolean = True); overload; + constructor Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean = True); overload; + property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner; + end; + + TObjectQueue<T: class> = class(TQueue<T>) + private + FObjectsOwner: Boolean; + protected + procedure Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); override; + public + constructor Create(AOwnsObjects: Boolean = True); overload; + constructor Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean = True); overload; + procedure Dequeue; + property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner; + end; + + TObjectStack<T: class> = class(TStack<T>) + private + FObjectsOwner: Boolean; + protected + procedure Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); override; + public + constructor Create(AOwnsObjects: Boolean = True); overload; + constructor Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean = True); overload; + procedure Pop; + property OwnsObjects: Boolean read FObjectsOwner write FObjectsOwner; + end; + + PObject = ^TObject; + +{$I inc\generics.dictionariesh.inc} + +function InCircularRange(ABottom, AItem, ATop: SizeInt): Boolean; + +implementation + +function InCircularRange(ABottom, AItem, ATop: SizeInt): Boolean; +begin + Result := + (ABottom < AItem) and (AItem <= ATop ) + or (ATop < ABottom) and (AItem > ABottom) + or (ATop < ABottom ) and (AItem <= ATop ); +end; + +{ TCustomArrayHelper<T> } + +class function TCustomArrayHelper<T>.BinarySearch(constref AValues: array of T; constref AItem: T; + out AFoundIndex: SizeInt; const AComparer: IComparer<T>): Boolean; +begin + Result := BinarySearch(AValues, AItem, AFoundIndex, AComparer, Low(AValues), Length(AValues)); +end; + +class function TCustomArrayHelper<T>.BinarySearch(constref AValues: array of T; constref AItem: T; + out AFoundIndex: SizeInt): Boolean; +begin + Result := BinarySearch(AValues, AItem, AFoundIndex, TComparerBugHack.Default, Low(AValues), Length(AValues)); +end; + +class procedure TCustomArrayHelper<T>.Sort(var AValues: array of T); +begin + QuickSort(AValues, Low(AValues), High(AValues), TComparerBugHack.Default); +end; + +class procedure TCustomArrayHelper<T>.Sort(var AValues: array of T; + const AComparer: IComparer<T>); +begin + QuickSort(AValues, Low(AValues), High(AValues), AComparer); +end; + +class procedure TCustomArrayHelper<T>.Sort(var AValues: array of T; + const AComparer: IComparer<T>; AIndex, ACount: SizeInt); +begin + if ACount <= 1 then + Exit; + QuickSort(AValues, AIndex, Pred(AIndex + ACount), AComparer); +end; + +{ TArrayHelper<T> } + +class procedure TArrayHelper<T>.QuickSort(var AValues: array of T; ALeft, ARight: SizeInt; + const AComparer: IComparer<T>); +var + I, J: SizeInt; + P, Q: T; +begin + if ((ARight - ALeft) <= 0) or (Length(AValues) = 0) then + Exit; + repeat + I := ALeft; + J := ARight; + P := AValues[ALeft + (ARight - ALeft) shr 1]; + repeat + while AComparer.Compare(AValues[I], P) < 0 do + I += 1; + while AComparer.Compare(AValues[J], P) > 0 do + J -= 1; + if I <= J then + begin + if I <> J then + begin + Q := AValues[I]; + AValues[I] := AValues[J]; + AValues[J] := Q; + end; + I += 1; + J -= 1; + end; + until I > J; + // sort the smaller range recursively + // sort the bigger range via the loop + // Reasons: memory usage is O(log(n)) instead of O(n) and loop is faster than recursion + if J - ALeft < ARight - I then + begin + if ALeft < J then + QuickSort(AValues, ALeft, J, AComparer); + ALeft := I; + end + else + begin + if I < ARight then + QuickSort(AValues, I, ARight, AComparer); + ARight := J; + end; + until ALeft >= ARight; +end; + +class function TArrayHelper<T>.BinarySearch(constref AValues: array of T; constref AItem: T; + out AFoundIndex: SizeInt; const AComparer: IComparer<T>; + AIndex, ACount: SizeInt): Boolean; +var + imin, imax, imid: Int32; + LCompare: SizeInt; +begin + // continually narrow search until just one element remains + imin := AIndex; + imax := Pred(AIndex + ACount); + + // http://en.wikipedia.org/wiki/Binary_search_algorithm + while (imin < imax) do + begin + imid := imin + ((imax - imin) shr 1); + + // code must guarantee the interval is reduced at each iteration + // assert(imid < imax); + // note: 0 <= imin < imax implies imid will always be less than imax + + LCompare := AComparer.Compare(AValues[imid], AItem); + // reduce the search + if (LCompare < 0) then + imin := imid + 1 + else + begin + imax := imid; + if LCompare = 0 then + begin + AFoundIndex := imid; + Exit(True); + end; + end; + end; + // At exit of while: + // if A[] is empty, then imax < imin + // otherwise imax == imin + + // deferred test for equality + + LCompare := AComparer.Compare(AValues[imin], AItem); + if (imax = imin) and (LCompare = 0) then + begin + AFoundIndex := imin; + Exit(True); + end + else + begin + AFoundIndex := -1; + Exit(False); + end; +end; + +{ TEnumerator<T> } + +function TEnumerator<T>.MoveNext: boolean; +begin + Exit(DoMoveNext); +end; + +{ TEnumerable<T> } + +function TEnumerable<T>.ToArrayImpl(ACount: SizeInt): TArray<T>; +var + i: SizeInt; + LEnumerator: TEnumerator<T>; +begin + SetLength(Result, ACount); + + try + LEnumerator := GetEnumerator; + + i := 0; + while LEnumerator.MoveNext do + begin + Result[i] := LEnumerator.Current; + Inc(i); + end; + finally + LEnumerator.Free; + end; +end; + +function TEnumerable<T>.GetEnumerator: TEnumerator; +begin + Exit(DoGetEnumerator); +end; + +function TEnumerable<T>.ToArray: TArray<T>; +var + LEnumerator: TEnumerator<T>; + LBuffer: TList<T>; +begin + LBuffer := TList<T>.Create; + try + LEnumerator := GetEnumerator; + + while LEnumerator.MoveNext do + LBuffer.Add(LEnumerator.Current); + + Result := LBuffer.ToArray; + finally + LBuffer.Free; + LEnumerator.Free; + end; +end; + +{ TCustomList<T> } + +function TCustomList<T>.PrepareAddingItem: SizeInt; +begin + Result := Length(FItems); + + if (FItemsLength < 4) and (Result < 4) then + SetLength(FItems, 4) + else if FItemsLength = High(FItemsLength) then + OutOfMemoryError + else if FItemsLength = Result then + SetLength(FItems, CUSTOM_LIST_CAPACITY_INC); + + Result := FItemsLength; + Inc(FItemsLength); +end; + +function TCustomList<T>.PrepareAddingRange(ACount: SizeInt): SizeInt; +begin + if ACount < 0 then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + if ACount = 0 then + Exit(FItemsLength - 1); + + if (FItemsLength = 0) and (Length(FItems) = 0) then + SetLength(FItems, 4) + else if FItemsLength = High(FItemsLength) then + OutOfMemoryError; + + Result := Length(FItems); + while Pred(FItemsLength + ACount) >= Result do + begin + SetLength(FItems, CUSTOM_LIST_CAPACITY_INC); + Result := Length(FItems); + end; + + Result := FItemsLength; + Inc(FItemsLength, ACount); +end; + +function TCustomList<T>.ToArray: TArray<T>; +begin + Result := ToArrayImpl(Count); +end; + +function TCustomList<T>.GetCount: SizeInt; +begin + Result := FItemsLength; +end; + +procedure TCustomList<T>.Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); +begin + if Assigned(FOnNotify) then + FOnNotify(Self, AValue, ACollectionNotification); +end; + +function TCustomList<T>.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; +begin + if (AIndex < 0) or (AIndex >= FItemsLength) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + Result := FItems[AIndex]; + Dec(FItemsLength); + + FItems[AIndex] := Default(T); + if AIndex <> FItemsLength then + begin + System.Move(FItems[AIndex + 1], FItems[AIndex], (FItemsLength - AIndex) * SizeOf(T)); + FillChar(FItems[FItemsLength], SizeOf(T), 0); + end; + + Notify(Result, ACollectionNotification); +end; + +function TCustomList<T>.GetCapacity: SizeInt; +begin + Result := Length(FItems); +end; + +{ TCustomListEnumerator<T> } + +function TCustomListEnumerator<T>.DoMoveNext: boolean; +begin + Inc(FIndex); + Result := (FList.FItemsLength <> 0) and (FIndex < FList.FItemsLength) +end; + +function TCustomListEnumerator<T>.DoGetCurrent: T; +begin + Result := GetCurrent; +end; + +function TCustomListEnumerator<T>.GetCurrent: T; +begin + Result := FList.FItems[FIndex]; +end; + +constructor TCustomListEnumerator<T>.Create(AList: TCustomList<T>); +begin + inherited Create; + FIndex := -1; + FList := AList; +end; + +{ TList<T> } + +constructor TList<T>.Create; +begin + FComparer := TComparer<T>.Default; +end; + +constructor TList<T>.Create(const AComparer: IComparer<T>); +begin + FComparer := AComparer; +end; + +constructor TList<T>.Create(ACollection: TEnumerable<T>); +var + LItem: T; +begin + Create; + for LItem in ACollection do + Add(LItem); +end; + +destructor TList<T>.Destroy; +begin + SetCapacity(0); +end; + +procedure TList<T>.SetCapacity(AValue: SizeInt); +begin + if AValue < Count then + Count := AValue; + + SetLength(FItems, AValue); +end; + +procedure TList<T>.SetCount(AValue: SizeInt); +begin + if AValue < 0 then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + if AValue > Capacity then + Capacity := AValue; + if AValue < Count then + DeleteRange(AValue, Count - AValue); + + FItemsLength := AValue; +end; + +function TList<T>.GetItem(AIndex: SizeInt): T; +begin + if (AIndex < 0) or (AIndex >= Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + Result := FItems[AIndex]; +end; + +procedure TList<T>.SetItem(AIndex: SizeInt; const AValue: T); +begin + if (AIndex < 0) or (AIndex >= Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + FItems[AIndex] := AValue; +end; + +function TList<T>.GetEnumerator: TEnumerator; +begin + Result := TEnumerator.Create(Self); +end; + +function TList<T>.DoGetEnumerator: {Generics.Collections.}TEnumerator<T>; +begin + Result := GetEnumerator; +end; + +function TList<T>.Add(constref AValue: T): SizeInt; +begin + Result := PrepareAddingItem; + FItems[Result] := AValue; + Notify(AValue, cnAdded); +end; + +procedure TList<T>.AddRange(constref AValues: array of T); +begin + InsertRange(Count, AValues); +end; + +procedure TList<T>.AddRange(const AEnumerable: IEnumerable<T>); +var + LValue: T; +begin + for LValue in AEnumerable do + Add(LValue); +end; + +procedure TList<T>.AddRange(AEnumerable: TEnumerable<T>); +var + LValue: T; +begin + for LValue in AEnumerable do + Add(LValue); +end; + +procedure TList<T>.Insert(AIndex: SizeInt; constref AValue: T); +begin + if (AIndex < 0) or (AIndex > Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + if AIndex <> PrepareAddingItem then + begin + System.Move(FItems[AIndex], FItems[AIndex + 1], ((Count - AIndex) - 1) * SizeOf(T)); + FillChar(FItems[AIndex], SizeOf(T), 0); + end; + + FItems[AIndex] := AValue; + Notify(AValue, cnAdded); +end; + +procedure TList<T>.InsertRange(AIndex: SizeInt; constref AValues: array of T); +var + i: SizeInt; + LLength: SizeInt; + LValue: ^T; +begin + if (AIndex < 0) or (AIndex > Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + LLength := Length(AValues); + if LLength = 0 then + Exit; + + if AIndex <> PrepareAddingRange(LLength) then + begin + System.Move(FItems[AIndex], FItems[AIndex + LLength], ((Count - AIndex) - LLength) * SizeOf(T)); + FillChar(FItems[AIndex], SizeOf(T) * LLength, 0); + end; + + LValue := @AValues[0]; + for i := AIndex to Pred(AIndex + LLength) do + begin + FItems[i] := LValue^; + Notify(LValue^, cnAdded); + Inc(LValue); + end; +end; + +procedure TList<T>.InsertRange(AIndex: SizeInt; const AEnumerable: IEnumerable<T>); +var + LValue: T; + i: SizeInt; +begin + if (AIndex < 0) or (AIndex > Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + i := 0; + for LValue in AEnumerable do + begin + Insert(Aindex + i, LValue); + Inc(i); + end; +end; + +procedure TList<T>.InsertRange(AIndex: SizeInt; const AEnumerable: TEnumerable<T>); +var + LValue: T; + i: SizeInt; +begin + if (AIndex < 0) or (AIndex > Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + i := 0; + for LValue in AEnumerable do + begin + Insert(Aindex + i, LValue); + Inc(i); + end; +end; + +function TList<T>.Remove(constref AValue: T): SizeInt; +begin + Result := IndexOf(AValue); + if Result >= 0 then + DoRemove(Result, cnRemoved); +end; + +procedure TList<T>.Delete(AIndex: SizeInt); +begin + DoRemove(AIndex, cnRemoved); +end; + +procedure TList<T>.DeleteRange(AIndex, ACount: SizeInt); +var + LDeleted: array of T; + i: SizeInt; + LMoveDelta: SizeInt; +begin + if ACount = 0 then + Exit; + + if (ACount < 0) or (AIndex < 0) or (AIndex + ACount > Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + SetLength(LDeleted, Count); + System.Move(FItems[AIndex], LDeleted[0], ACount * SizeOf(T)); + + LMoveDelta := Count - (AIndex + ACount); + + if LMoveDelta = 0 then + FillChar(FItems[AIndex], ACount * SizeOf(T), #0) + else + begin + System.Move(FItems[AIndex + ACount], FItems[AIndex], LMoveDelta * SizeOf(T)); + FillChar(FItems[Count - ACount], ACount * SizeOf(T), #0); + end; + + FItemsLength -= ACount; + + for i := 0 to High(LDeleted) do + Notify(LDeleted[i], cnRemoved); +end; + +function TList<T>.ExtractIndex(const AIndex: SizeInt): T; +begin + Result := DoRemove(AIndex, cnExtracted); +end; + +function TList<T>.Extract(constref AValue: T): T; +var + LIndex: SizeInt; +begin + LIndex := IndexOf(AValue); + if LIndex < 0 then + Exit(Default(T)); + + Result := DoRemove(LIndex, cnExtracted); +end; + +procedure TList<T>.Exchange(AIndex1, AIndex2: SizeInt); +var + LTemp: T; +begin + LTemp := FItems[AIndex1]; + FItems[AIndex1] := FItems[AIndex2]; + FItems[AIndex2] := LTemp; +end; + +procedure TList<T>.Move(AIndex, ANewIndex: SizeInt); +var + LTemp: T; +begin + if ANewIndex = AIndex then + Exit; + + if (ANewIndex < 0) or (ANewIndex >= Count) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + LTemp := FItems[AIndex]; + FItems[AIndex] := Default(T); + + if AIndex < ANewIndex then + System.Move(FItems[Succ(AIndex)], FItems[AIndex], (ANewIndex - AIndex) * SizeOf(T)) + else + System.Move(FItems[ANewIndex], FItems[Succ(ANewIndex)], (AIndex - ANewIndex) * SizeOf(T)); + + FillChar(FItems[ANewIndex], SizeOf(T), #0); + FItems[ANewIndex] := LTemp; +end; + +function TList<T>.First: T; +begin + Result := Items[0]; +end; + +function TList<T>.Last: T; +begin + Result := Items[Pred(Count)]; +end; + +procedure TList<T>.Clear; +begin + SetCount(0); + SetCapacity(0); +end; + +procedure TList<T>.TrimExcess; +begin + SetCapacity(Count); +end; + +function TList<T>.Contains(constref AValue: T): Boolean; +begin + Result := IndexOf(AValue) >= 0; +end; + +function TList<T>.IndexOf(constref AValue: T): SizeInt; +var + i: SizeInt; +begin + for i := 0 to Count - 1 do + if FComparer.Compare(AValue, FItems[i]) = 0 then + Exit(i); + Result := -1; +end; + +function TList<T>.LastIndexOf(constref AValue: T): SizeInt; +var + i: SizeInt; +begin + for i := Count - 1 downto 0 do + if FComparer.Compare(AValue, FItems[i]) = 0 then + Exit(i); + Result := -1; +end; + +procedure TList<T>.Reverse; +var + a, b: SizeInt; + LTemp: T; +begin + a := 0; + b := Count - 1; + while a < b do + begin + LTemp := FItems[a]; + FItems[a] := FItems[b]; + FItems[b] := LTemp; + Inc(a); + Dec(b); + end; +end; + +procedure TList<T>.Sort; +begin + TArrayHelperBugHack.Sort(FItems, FComparer, 0, Count); +end; + +procedure TList<T>.Sort(const AComparer: IComparer<T>); +begin + TArrayHelperBugHack.Sort(FItems, AComparer, 0, Count); +end; + +function TList<T>.BinarySearch(constref AItem: T; out AIndex: SizeInt): Boolean; +begin + Result := TArrayHelperBugHack.BinarySearch(FItems, AItem, AIndex); +end; + +function TList<T>.BinarySearch(constref AItem: T; out AIndex: SizeInt; const AComparer: IComparer<T>): Boolean; +begin + Result := TArrayHelperBugHack.BinarySearch(FItems, AItem, AIndex, AComparer); +end; + +{ TQueue<T>.TEnumerator } + +constructor TQueue<T>.TEnumerator.Create(AQueue: TQueue<T>); +begin + inherited Create(AQueue); + + FIndex := Pred(AQueue.FLow); +end; + +{ TQueue<T> } + +function TQueue<T>.GetEnumerator: TEnumerator; +begin + Result := TEnumerator.Create(Self); +end; + +function TQueue<T>.DoGetEnumerator: {Generics.Collections.}TEnumerator<T>; +begin + Result := GetEnumerator; +end; + +function TQueue<T>.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; +begin + Result := FItems[AIndex]; + FItems[AIndex] := Default(T); + Notify(Result, ACollectionNotification); + FLow += 1; + if FLow = FItemsLength then + begin + FLow := 0; + FItemsLength := 0; + end; +end; + +procedure TQueue<T>.SetCapacity(AValue: SizeInt); +begin + if AValue < Count then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + if AValue = FItemsLength then + Exit; + + if (Count > 0) and (FLow > 0) then + begin + Move(FItems[FLow], FItems[0], Count * SizeOf(T)); + FillChar(FItems[Count], (FItemsLength - Count) * SizeOf(T), #0); + end; + + SetLength(FItems, AValue); + FItemsLength := Count; + FLow := 0; +end; + +function TQueue<T>.GetCount: SizeInt; +begin + Result := FItemsLength - FLow; +end; + +constructor TQueue<T>.Create(ACollection: TEnumerable<T>); +var + LItem: T; +begin + for LItem in ACollection do + Enqueue(LItem); +end; + +destructor TQueue<T>.Destroy; +begin + Clear; +end; + +procedure TQueue<T>.Enqueue(constref AValue: T); +var + LIndex: SizeInt; +begin + LIndex := PrepareAddingItem; + FItems[LIndex] := AValue; + Notify(AValue, cnAdded); +end; + +function TQueue<T>.Dequeue: T; +begin + Result := DoRemove(FLow, cnRemoved); +end; + +function TQueue<T>.Extract: T; +begin + Result := DoRemove(FLow, cnExtracted); +end; + +function TQueue<T>.Peek: T; +begin + if (Count = 0) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + Result := FItems[FLow]; +end; + +procedure TQueue<T>.Clear; +begin + while Count <> 0 do + Dequeue; + FLow := 0; + FItemsLength := 0; +end; + +procedure TQueue<T>.TrimExcess; +begin + SetCapacity(Count); +end; + +{ TStack<T> } + +function TStack<T>.GetEnumerator: TEnumerator; +begin + Result := TEnumerator.Create(Self); +end; + +function TStack<T>.DoGetEnumerator: {Generics.Collections.}TEnumerator<T>; +begin + Result := GetEnumerator; +end; + +constructor TStack<T>.Create(ACollection: TEnumerable<T>); +var + LItem: T; +begin + for LItem in ACollection do + Push(LItem); +end; + +function TStack<T>.DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): T; +begin + if AIndex < 0 then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + Result := FItems[AIndex]; + FItems[AIndex] := Default(T); + FItemsLength -= 1; + Notify(Result, ACollectionNotification); +end; + +destructor TStack<T>.Destroy; +begin + Clear; +end; + +procedure TStack<T>.Clear; +begin + while Count <> 0 do + Pop; +end; + +procedure TStack<T>.SetCapacity(AValue: SizeInt); +begin + if AValue < Count then + AValue := Count; + + SetLength(FItems, AValue); +end; + +procedure TStack<T>.Push(constref AValue: T); +var + LIndex: SizeInt; +begin + LIndex := PrepareAddingItem; + FItems[LIndex] := AValue; + Notify(AValue, cnAdded); +end; + +function TStack<T>.Pop: T; +begin + Result := DoRemove(FItemsLength - 1, cnRemoved); +end; + +function TStack<T>.Peek: T; +begin + if (Count = 0) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + Result := FItems[FItemsLength - 1]; +end; + +function TStack<T>.Extract: T; +begin + Result := DoRemove(FItemsLength - 1, cnExtracted); +end; + +procedure TStack<T>.TrimExcess; +begin + SetCapacity(Count); +end; + +{ TObjectList<T> } + +procedure TObjectList<T>.Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); +begin + inherited Notify(AValue, ACollectionNotification); + + if FObjectsOwner and (ACollectionNotification = cnRemoved) then + TObject(AValue).Free; +end; + +constructor TObjectList<T>.Create(AOwnsObjects: Boolean); +begin + inherited Create; + + FObjectsOwner := AOwnsObjects; +end; + +constructor TObjectList<T>.Create(const AComparer: IComparer<T>; AOwnsObjects: Boolean); +begin + inherited Create(AComparer); + + FObjectsOwner := AOwnsObjects; +end; + +constructor TObjectList<T>.Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean); +begin + inherited Create(ACollection); + + FObjectsOwner := AOwnsObjects; +end; + +{ TObjectQueue<T> } + +procedure TObjectQueue<T>.Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); +begin + inherited Notify(AValue, ACollectionNotification); + if FObjectsOwner and (ACollectionNotification = cnRemoved) then + TObject(AValue).Free; +end; + +constructor TObjectQueue<T>.Create(AOwnsObjects: Boolean); +begin + inherited Create; + + FObjectsOwner := AOwnsObjects; +end; + +constructor TObjectQueue<T>.Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean); +begin + inherited Create(ACollection); + + FObjectsOwner := AOwnsObjects; +end; + +procedure TObjectQueue<T>.Dequeue; +begin + inherited Dequeue; +end; + +{ TObjectStack<T> } + +procedure TObjectStack<T>.Notify(constref AValue: T; ACollectionNotification: TCollectionNotification); +begin + inherited Notify(AValue, ACollectionNotification); + if FObjectsOwner and (ACollectionNotification = cnRemoved) then + TObject(AValue).Free; +end; + +constructor TObjectStack<T>.Create(AOwnsObjects: Boolean); +begin + inherited Create; + + FObjectsOwner := AOwnsObjects; +end; + +constructor TObjectStack<T>.Create(ACollection: TEnumerable<T>; AOwnsObjects: Boolean); +begin + inherited Create(ACollection); + + FObjectsOwner := AOwnsObjects; +end; + +procedure TObjectStack<T>.Pop; +begin + inherited Pop; +end; + +{$I inc\generics.dictionaries.inc} + +end. diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.defaults.pas b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.defaults.pas new file mode 100644 index 000000000..14ec05753 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.defaults.pas @@ -0,0 +1,3372 @@ +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2014 by Maciej Izak (hnb) + member of the Free Sparta development team (http://freesparta.com) + + Copyright(c) 2004-2014 DaThoX + + It contains the Free Pascal generics library + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} + +unit Generics.Defaults; + +{$MODE DELPHI}{$H+} +{$POINTERMATH ON} +{$MACRO ON} +{$COPERATORS ON} +{$HINTS OFF} +{$WARNINGS OFF} +{$NOTES OFF} + +interface + +uses + Classes, SysUtils, Generics.Hashes, TypInfo, Variants, Math, Generics.Strings, Generics.Helpers; + +type + IComparer<T> = interface + function Compare(constref Left, Right: T): Integer; overload; + end; + + TOnComparison<T> = function(constref Left, Right: T): Integer of object; + TComparisonFunc<T> = function(constref Left, Right: T): Integer; + + TComparer<T> = class(TInterfacedObject, IComparer<T>) + public + class function Default: IComparer<T>; static; + function Compare(constref ALeft, ARight: T): Integer; virtual; abstract; overload; + + class function Construct(const AComparison: TOnComparison<T>): IComparer<T>; overload; + class function Construct(const AComparison: TComparisonFunc<T>): IComparer<T>; overload; + end; + + TDelegatedComparerEvents<T> = class(TComparer<T>) + private + FComparison: TOnComparison<T>; + public + function Compare(constref ALeft, ARight: T): Integer; override; + constructor Create(AComparison: TOnComparison<T>); + end; + + TDelegatedComparerFunc<T> = class(TComparer<T>) + private + FComparison: TComparisonFunc<T>; + public + function Compare(constref ALeft, ARight: T): Integer; override; + constructor Create(AComparison: TComparisonFunc<T>); + end; + + IEqualityComparer<T> = interface + function Equals(constref ALeft, ARight: T): Boolean; + function GetHashCode(constref AValue: T): UInt32; + end; + + IExtendedEqualityComparer<T> = interface(IEqualityComparer<T>) + procedure GetHashList(constref AValue: T; AHashList: PUInt32); // for double hashing and more + end; + + ShortString1 = string[1]; + ShortString2 = string[2]; + ShortString3 = string[3]; + + { TAbstractInterface } + + TInterface = class + public + function QueryInterface(constref {%H-}IID: TGUID;{%H-} out Obj): HResult; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual; + function _AddRef: Integer; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual; abstract; + function _Release: Integer; {$IFNDEF WINDOWS}cdecl{$ELSE}stdcall{$ENDIF}; virtual; abstract; + end; + + { TRawInterface } + + TRawInterface = class(TInterface) + public + function _AddRef: Integer; override; + function _Release: Integer; override; + end; + + { TComTypeSizeInterface } + + // INTERNAL USE ONLY! + TComTypeSizeInterface = class(TInterface) + public + // warning ! self as PSpoofInterfacedTypeSizeObject + function _AddRef: Integer; override; + // warning ! self as PSpoofInterfacedTypeSizeObject + function _Release: Integer; override; + end; + + { TSingletonImplementation } + + TSingletonImplementation = class(TRawInterface, IInterface) + public + function QueryInterface(constref IID: TGUID; out Obj): HResult; override; + end; + + TCompare = class + protected + // warning ! self as PSpoofInterfacedTypeSizeObject + class function _Binary(constref ALeft, ARight): Integer; + // warning ! self as PSpoofInterfacedTypeSizeObject + class function _DynArray(constref ALeft, ARight: Pointer): Integer; + public + class function Integer(constref ALeft, ARight: Integer): Integer; + class function Int8(constref ALeft, ARight: Int8): Integer; + class function Int16(constref ALeft, ARight: Int16): Integer; + class function Int32(constref ALeft, ARight: Int32): Integer; + class function Int64(constref ALeft, ARight: Int64): Integer; + class function UInt8(constref ALeft, ARight: UInt8): Integer; + class function UInt16(constref ALeft, ARight: UInt16): Integer; + class function UInt32(constref ALeft, ARight: UInt32): Integer; + class function UInt64(constref ALeft, ARight: UInt64): Integer; + class function Single(constref ALeft, ARight: Single): Integer; + class function Double(constref ALeft, ARight: Double): Integer; + class function Extended(constref ALeft, ARight: Extended): Integer; + class function Currency(constref ALeft, ARight: Currency): Integer; + class function Comp(constref ALeft, ARight: Comp): Integer; + class function Binary(constref ALeft, ARight; const ASize: SizeInt): Integer; + class function DynArray(constref ALeft, ARight: Pointer; const AElementSize: SizeInt): Integer; + class function ShortString1(constref ALeft, ARight: ShortString1): Integer; + class function ShortString2(constref ALeft, ARight: ShortString2): Integer; + class function ShortString3(constref ALeft, ARight: ShortString3): Integer; + class function &String(constref ALeft, ARight: string): Integer; + class function ShortString(constref ALeft, ARight: OpenString): Integer; + class function AnsiString(constref ALeft, ARight: AnsiString): Integer; + class function WideString(constref ALeft, ARight: WideString): Integer; + class function UnicodeString(constref ALeft, ARight: UnicodeString): Integer; + class function Method(constref ALeft, ARight: TMethod): Integer; + class function Variant(constref ALeft, ARight: PVariant): Integer; + class function Pointer(constref ALeft, ARight: PtrUInt): Integer; + end; + + { TEquals } + + TEquals = class + protected + // warning ! self as PSpoofInterfacedTypeSizeObject + class function _Binary(constref ALeft, ARight): Boolean; + // warning ! self as PSpoofInterfacedTypeSizeObject + class function _DynArray(constref ALeft, ARight: Pointer): Boolean; + public + class function Integer(constref ALeft, ARight: Integer): Boolean; + class function Int8(constref ALeft, ARight: Int8): Boolean; + class function Int16(constref ALeft, ARight: Int16): Boolean; + class function Int32(constref ALeft, ARight: Int32): Boolean; + class function Int64(constref ALeft, ARight: Int64): Boolean; + class function UInt8(constref ALeft, ARight: UInt8): Boolean; + class function UInt16(constref ALeft, ARight: UInt16): Boolean; + class function UInt32(constref ALeft, ARight: UInt32): Boolean; + class function UInt64(constref ALeft, ARight: UInt64): Boolean; + class function Single(constref ALeft, ARight: Single): Boolean; + class function Double(constref ALeft, ARight: Double): Boolean; + class function Extended(constref ALeft, ARight: Extended): Boolean; + class function Currency(constref ALeft, ARight: Currency): Boolean; + class function Comp(constref ALeft, ARight: Comp): Boolean; + class function Binary(constref ALeft, ARight; const ASize: SizeInt): Boolean; + class function DynArray(constref ALeft, ARight: Pointer; const AElementSize: SizeInt): Boolean; + class function &Class(constref ALeft, ARight: TObject): Boolean; + class function ShortString1(constref ALeft, ARight: ShortString1): Boolean; + class function ShortString2(constref ALeft, ARight: ShortString2): Boolean; + class function ShortString3(constref ALeft, ARight: ShortString3): Boolean; + class function &String(constref ALeft, ARight: String): Boolean; + class function ShortString(constref ALeft, ARight: OpenString): Boolean; + class function AnsiString(constref ALeft, ARight: AnsiString): Boolean; + class function WideString(constref ALeft, ARight: WideString): Boolean; + class function UnicodeString(constref ALeft, ARight: UnicodeString): Boolean; + class function Method(constref ALeft, ARight: TMethod): Boolean; + class function Variant(constref ALeft, ARight: PVariant): Boolean; + class function Pointer(constref ALeft, ARight: PtrUInt): Boolean; + end; + + TComparerFactoryClass = class of TComparerFactory; + + THashFactoryClass = class of THashFactory; + + TExtendedHashFactoryClass = class of TExtendedHashFactory; + + { TComparerFactory } + +{$DEFINE STD_RAW_INTERFACE_METHODS := + QueryInterface: @TRawInterface.QueryInterface; + _AddRef : @TRawInterface._AddRef; + _Release : @TRawInterface._Release +} + +{$DEFINE STD_COM_TYPESIZE_INTERFACE_METHODS := + QueryInterface: @TComTypeSizeInterface.QueryInterface; + _AddRef : @TComTypeSizeInterface._AddRef; + _Release : @TComTypeSizeInterface._Release +} + +{$DEFINE STD_COM_INTERFACE_METHODS := + QueryInterface: @QueryInterface; + _AddRef : @AddRef; + _Release : @Release +} + + TGetHashListOptions = set of (ghloHashListAsInitData); + + TComparerFactory = class abstract + protected + class function GetID: Integer; virtual; abstract; // any hash factory must have + private + function LookupEqualityComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; virtual; abstract; + function LookupExtendedEqualityComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; virtual; abstract; + + function SelectIntegerEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; virtual; abstract; + function SelectFloatEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; virtual; abstract; + function SelectShortStringEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; virtual; abstract; + function SelectBinaryEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; virtual; abstract; + function SelectDynArrayEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; virtual; abstract; + private type + PSpoofInterfacedTypeSizeObject = ^TSpoofInterfacedTypeSizeObject; + TSpoofInterfacedTypeSizeObject = record + VMT: Pointer; + RefCount: Integer; + Size: SizeInt; + end; + + PInstance = ^TInstance; + TInstance = record + Selector: Boolean; + Instance: Pointer; + + class function Create(ASelector: Boolean; AInstance: Pointer): TComparerFactory.TInstance; static; + end; + + PComparerVMT = ^TComparerVMT; + TComparerVMT = packed record + QueryInterface: Pointer; + _AddRef: Pointer; + _Release: Pointer; + Compare: Pointer; + end; + + TSelectFunc = function(ATypeData: PTypeData; ASize: SizeInt): Pointer; + + private + class function CreateInterface(AVMT: Pointer; ASize: SizeInt): PSpoofInterfacedTypeSizeObject; static; + + class function SelectIntegerComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static; + class function SelectInt64Comparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static; + class function SelectFloatComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static; + class function SelectShortStringComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static; + class function SelectBinaryComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static; + class function SelectDynArrayComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; static; + + private const + // IComparer VMT + Comparer_Int8_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Int8); + Comparer_Int16_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Int16 ); + Comparer_Int32_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Int32 ); + Comparer_Int64_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Int64 ); + Comparer_UInt8_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.UInt8 ); + Comparer_UInt16_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.UInt16); + Comparer_UInt32_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.UInt32); + Comparer_UInt64_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.UInt64); + + Comparer_Single_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Single ); + Comparer_Double_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Double ); + Comparer_Extended_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Extended); + + Comparer_Currency_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Currency); + Comparer_Comp_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Comp ); + + Comparer_Binary_VMT : TComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Compare: @TCompare._Binary ); + Comparer_DynArray_VMT: TComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Compare: @TCompare._DynArray); + + Comparer_ShortString1_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.ShortString1 ); + Comparer_ShortString2_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.ShortString2 ); + Comparer_ShortString3_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.ShortString3 ); + Comparer_ShortString_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.ShortString ); + Comparer_AnsiString_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.AnsiString ); + Comparer_WideString_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.WideString ); + Comparer_UnicodeString_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.UnicodeString); + + Comparer_Method_VMT : TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Method ); + Comparer_Variant_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Variant); + Comparer_Pointer_VMT: TComparerVMT = (STD_RAW_INTERFACE_METHODS; Compare: @TCompare.Pointer); + + // Instances + Comparer_Int8_Instance : Pointer = @Comparer_Int8_VMT ; + Comparer_Int16_Instance : Pointer = @Comparer_Int16_VMT ; + Comparer_Int32_Instance : Pointer = @Comparer_Int32_VMT ; + Comparer_Int64_Instance : Pointer = @Comparer_Int64_VMT ; + Comparer_UInt8_Instance : Pointer = @Comparer_UInt8_VMT ; + Comparer_UInt16_Instance: Pointer = @Comparer_UInt16_VMT; + Comparer_UInt32_Instance: Pointer = @Comparer_UInt32_VMT; + Comparer_UInt64_Instance: Pointer = @Comparer_UInt64_VMT; + + Comparer_Single_Instance : Pointer = @Comparer_Single_VMT ; + Comparer_Double_Instance : Pointer = @Comparer_Double_VMT ; + Comparer_Extended_Instance: Pointer = @Comparer_Extended_VMT; + + Comparer_Currency_Instance: Pointer = @Comparer_Currency_VMT; + Comparer_Comp_Instance : Pointer = @Comparer_Comp_VMT ; + + //Comparer_Binary_Instance : Pointer = @Comparer_Binary_VMT ; // dynamic instance + //Comparer_DynArray_Instance: Pointer = @Comparer_DynArray_VMT; // dynamic instance + + Comparer_ShortString1_Instance : Pointer = @Comparer_ShortString1_VMT ; + Comparer_ShortString2_Instance : Pointer = @Comparer_ShortString2_VMT ; + Comparer_ShortString3_Instance : Pointer = @Comparer_ShortString3_VMT ; + Comparer_ShortString_Instance : Pointer = @Comparer_ShortString_VMT ; + Comparer_AnsiString_Instance : Pointer = @Comparer_AnsiString_VMT ; + Comparer_WideString_Instance : Pointer = @Comparer_WideString_VMT ; + Comparer_UnicodeString_Instance: Pointer = @Comparer_UnicodeString_VMT; + + Comparer_Method_Instance : Pointer = @Comparer_Method_VMT ; + Comparer_Variant_Instance: Pointer = @Comparer_Variant_VMT; + Comparer_Pointer_Instance: Pointer = @Comparer_Pointer_VMT; + + ComparerInstances: array[TTypeKind] of TInstance = + ( + // tkUnknown + (Selector: True; Instance: @TComparerFactory.SelectBinaryComparer), + // tkInteger + (Selector: True; Instance: @TComparerFactory.SelectIntegerComparer), + // tkChar + (Selector: False; Instance: @Comparer_UInt8_Instance), + // tkEnumeration + (Selector: True; Instance: @TComparerFactory.SelectIntegerComparer), + // tkFloat + (Selector: True; Instance: @TComparerFactory.SelectFloatComparer), + // tkSet + (Selector: True; Instance: @TComparerFactory.SelectBinaryComparer), + // tkMethod + (Selector: False; Instance: @Comparer_Method_Instance), + // tkSString + (Selector: True; Instance: @TComparerFactory.SelectShortStringComparer), + // tkLString - only internal use / deprecated in compiler + (Selector: False; Instance: @Comparer_AnsiString_Instance), // <- unsure + // tkAString + (Selector: False; Instance: @Comparer_AnsiString_Instance), + // tkWString + (Selector: False; Instance: @Comparer_WideString_Instance), + // tkVariant + (Selector: False; Instance: @Comparer_Variant_Instance), + // tkArray + (Selector: True; Instance: @TComparerFactory.SelectBinaryComparer), + // tkRecord + (Selector: True; Instance: @TComparerFactory.SelectBinaryComparer), + // tkInterface + (Selector: False; Instance: @Comparer_Pointer_Instance), + // tkClass + (Selector: False; Instance: @Comparer_Pointer_Instance), + // tkObject + (Selector: True; Instance: @TComparerFactory.SelectBinaryComparer), + // tkWChar + (Selector: False; Instance: @Comparer_UInt16_Instance), + // tkBool + (Selector: True; Instance: @TComparerFactory.SelectIntegerComparer), + // tkInt64 + (Selector: False; Instance: @Comparer_Int64_Instance), + // tkQWord + (Selector: False; Instance: @Comparer_UInt64_Instance), + // tkDynArray + (Selector: True; Instance: @TComparerFactory.SelectDynArrayComparer), + // tkInterfaceRaw + (Selector: False; Instance: @Comparer_Pointer_Instance), + // tkProcVar + (Selector: False; Instance: @Comparer_Pointer_Instance), + // tkUString + (Selector: False; Instance: @Comparer_UnicodeString_Instance), + // tkUChar - WTF? ... http://bugs.freepascal.org/view.php?id=24609 + (Selector: False; Instance: @Comparer_UInt16_Instance), // <- unsure maybe Comparer_UInt32_Instance + // tkHelper + (Selector: False; Instance: @Comparer_Pointer_Instance), + // tkFile + (Selector: True; Instance: @TComparerFactory.SelectBinaryComparer), // <- unsure what type? + // tkClassRef + (Selector: False; Instance: @Comparer_Pointer_Instance), + // tkPointer + (Selector: False; Instance: @Comparer_Pointer_Instance) + ); + public + constructor Create; virtual; + class function LookupComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; static; + + class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; virtual; abstract; reintroduce; + class procedure GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; AOptions: TGetHashListOptions = []); virtual; abstract; + //class function GetHashCodeEx(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32; virtual; abstract; + //class procedure GetHashListEx(AKey: Pointer; ASize: SizeInt; AHashListAndInitValues: PUInt32; ACount: Integer); virtual; abstract; + + class function Register(const AComparerFactory: TComparerFactoryClass): Integer; + end; + + { THashCode } + + THashCode = class(TComparerFactory) + private type + PPEqualityComparerVMT = ^PEqualityComparerVMT; + PEqualityComparerVMT = ^TEqualityComparerVMT; + TEqualityComparerVMT = packed record + QueryInterface: Pointer; + _AddRef: Pointer; + _Release: Pointer; + Equals: Pointer; + GetHashCode: Pointer; + __Reserved: Pointer; // initially or TExtendedEqualityComparerVMT compatibility + // (important when ExtendedEqualityComparer is calling Binary method) + __ClassRef: THashFactoryClass; // hidden field in VMT. For class ref THashFactoryClass + end; + + { TInstance } + TSelectMethod = function(ATypeData: PTypeData; ASize: SizeInt): Pointer of object; + private +(*********************************************************************************************************************** + Hashes +(**********************************************************************************************************************) + + class function Int8 (constref AValue: Int8 ): UInt32; overload; + class function Int16 (constref AValue: Int16 ): UInt32; overload; + class function Int32 (constref AValue: Int32 ): UInt32; overload; + class function Int64 (constref AValue: Int64 ): UInt32; overload; + class function UInt8 (constref AValue: UInt8 ): UInt32; overload; + class function UInt16 (constref AValue: UInt16 ): UInt32; overload; + class function UInt32 (constref AValue: UInt32 ): UInt32; overload; + class function UInt64 (constref AValue: UInt64 ): UInt32; overload; + class function Single (constref AValue: Single ): UInt32; overload; + class function Double (constref AValue: Double ): UInt32; overload; + class function Extended (constref AValue: Extended ): UInt32; overload; + class function Currency (constref AValue: Currency ): UInt32; overload; + class function Comp (constref AValue: Comp ): UInt32; overload; + // warning ! self as PSpoofInterfacedTypeSizeObject + class function Binary (constref AValue ): UInt32; overload; + // warning ! self as PSpoofInterfacedTypeSizeObject + class function DynArray (constref AValue: Pointer ): UInt32; overload; + class function &Class (constref AValue: TObject ): UInt32; overload; + class function ShortString1 (constref AValue: ShortString1 ): UInt32; overload; + class function ShortString2 (constref AValue: ShortString2 ): UInt32; overload; + class function ShortString3 (constref AValue: ShortString3 ): UInt32; overload; + class function ShortString (constref AValue: OpenString ): UInt32; overload; + class function AnsiString (constref AValue: AnsiString ): UInt32; overload; + class function WideString (constref AValue: WideString ): UInt32; overload; + class function UnicodeString(constref AValue: UnicodeString): UInt32; overload; + class function Method (constref AValue: TMethod ): UInt32; overload; + class function Variant (constref AValue: PVariant ): UInt32; overload; + class function Pointer (constref AValue: Pointer ): UInt32; overload; + public + const MAX_HASHLIST_COUNT = 1; + const HASH_FUNCTIONS_COUNT = 1; + const HASHLIST_COUNT_PER_FUNCTION: array[1..HASH_FUNCTIONS_COUNT] of Integer = (1); + const HASH_FUNCTIONS_MASK_SIZE = 1; + end; + + TExtendedHashCode = class(THashCode) + private type + PPExtendedEqualityComparerVMT = ^PExtendedEqualityComparerVMT; + PExtendedEqualityComparerVMT = ^TExtendedEqualityComparerVMT; + TExtendedEqualityComparerVMT = packed record + QueryInterface: Pointer; + _AddRef: Pointer; + _Release: Pointer; + Equals: Pointer; + GetHashCode: Pointer; + GetHashList: Pointer; + __ClassRef: TExtendedHashFactoryClass; // hidden field in VMT. For class ref THashFactoryClass + end; + private +(*********************************************************************************************************************** + Hashes 2 +(**********************************************************************************************************************) + + class procedure Int8 (constref AValue: Int8 ; AHashList: PUInt32); overload; + class procedure Int16 (constref AValue: Int16 ; AHashList: PUInt32); overload; + class procedure Int32 (constref AValue: Int32 ; AHashList: PUInt32); overload; + class procedure Int64 (constref AValue: Int64 ; AHashList: PUInt32); overload; + class procedure UInt8 (constref AValue: UInt8 ; AHashList: PUInt32); overload; + class procedure UInt16 (constref AValue: UInt16 ; AHashList: PUInt32); overload; + class procedure UInt32 (constref AValue: UInt32 ; AHashList: PUInt32); overload; + class procedure UInt64 (constref AValue: UInt64 ; AHashList: PUInt32); overload; + class procedure Single (constref AValue: Single ; AHashList: PUInt32); overload; + class procedure Double (constref AValue: Double ; AHashList: PUInt32); overload; + class procedure Extended (constref AValue: Extended ; AHashList: PUInt32); overload; + class procedure Currency (constref AValue: Currency ; AHashList: PUInt32); overload; + class procedure Comp (constref AValue: Comp ; AHashList: PUInt32); overload; + // warning ! self as PSpoofInterfacedTypeSizeObject + class procedure Binary (constref AValue ; AHashList: PUInt32); overload; + // warning ! self as PSpoofInterfacedTypeSizeObject + class procedure DynArray (constref AValue: Pointer ; AHashList: PUInt32); overload; + class procedure &Class (constref AValue: TObject ; AHashList: PUInt32); overload; + class procedure ShortString1 (constref AValue: ShortString1 ; AHashList: PUInt32); overload; + class procedure ShortString2 (constref AValue: ShortString2 ; AHashList: PUInt32); overload; + class procedure ShortString3 (constref AValue: ShortString3 ; AHashList: PUInt32); overload; + class procedure ShortString (constref AValue: OpenString ; AHashList: PUInt32); overload; + class procedure AnsiString (constref AValue: AnsiString ; AHashList: PUInt32); overload; + class procedure WideString (constref AValue: WideString ; AHashList: PUInt32); overload; + class procedure UnicodeString(constref AValue: UnicodeString; AHashList: PUInt32); overload; + class procedure Method (constref AValue: TMethod ; AHashList: PUInt32); overload; + class procedure Variant (constref AValue: PVariant ; AHashList: PUInt32); overload; + class procedure Pointer (constref AValue: Pointer ; AHashList: PUInt32); overload; + end; + + { THashFactory } + +{$DEFINE HASH_FACTORY := PPEqualityComparerVMT(Self)^.__ClassRef} +{$DEFINE EXTENDED_HASH_FACTORY := PPExtendedEqualityComparerVMT(Self)^.__ClassRef} + + THashFactory = class(THashCode) + private + function SelectIntegerEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override; + function SelectFloatEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override; + function SelectShortStringEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override; + function SelectBinaryEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override; + function SelectDynArrayEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override; + private const + // IEqualityComparer VMT templates +{$WARNINGS OFF} + EqualityComparer_Int8_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int8 ; GetHashCode: @THashCode.Int8 ); + EqualityComparer_Int16_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int16 ; GetHashCode: @THashCode.Int16 ); + EqualityComparer_Int32_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int32 ; GetHashCode: @THashCode.Int32 ); + EqualityComparer_Int64_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int64 ; GetHashCode: @THashCode.Int64 ); + EqualityComparer_UInt8_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt8 ; GetHashCode: @THashCode.UInt8 ); + EqualityComparer_UInt16_VMT: TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt16; GetHashCode: @THashCode.UInt16); + EqualityComparer_UInt32_VMT: TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt32; GetHashCode: @THashCode.UInt32); + EqualityComparer_UInt64_VMT: TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt64; GetHashCode: @THashCode.UInt64); + + EqualityComparer_Single_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Single ; GetHashCode: @THashCode.Single ); + EqualityComparer_Double_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Double ; GetHashCode: @THashCode.Double ); + EqualityComparer_Extended_VMT: TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Extended; GetHashCode: @THashCode.Extended); + + EqualityComparer_Currency_VMT: TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Currency; GetHashCode: @THashCode.Currency); + EqualityComparer_Comp_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Comp ; GetHashCode: @THashCode.Comp ); + + EqualityComparer_Binary_VMT : TEqualityComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Equals: @TEquals._Binary ; GetHashCode: @THashCode.Binary ); + EqualityComparer_DynArray_VMT: TEqualityComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Equals: @TEquals._DynArray; GetHashCode: @THashCode.DynArray); + + EqualityComparer_Class_VMT: TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.&Class; GetHashCode: @THashCode.&Class); + + EqualityComparer_ShortString1_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString1 ; GetHashCode: @THashCode.ShortString1 ); + EqualityComparer_ShortString2_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString2 ; GetHashCode: @THashCode.ShortString2 ); + EqualityComparer_ShortString3_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString3 ; GetHashCode: @THashCode.ShortString3 ); + EqualityComparer_ShortString_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString ; GetHashCode: @THashCode.ShortString ); + EqualityComparer_AnsiString_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.AnsiString ; GetHashCode: @THashCode.AnsiString ); + EqualityComparer_WideString_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.WideString ; GetHashCode: @THashCode.WideString ); + EqualityComparer_UnicodeString_VMT: TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UnicodeString; GetHashCode: @THashCode.UnicodeString); + + EqualityComparer_Method_VMT : TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Method ; GetHashCode: @THashCode.Method ); + EqualityComparer_Variant_VMT: TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Variant; GetHashCode: @THashCode.Variant); + EqualityComparer_Pointer_VMT: TEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Pointer; GetHashCode: @THashCode.Pointer); +{$WARNINGS ON} + private var + // IEqualityComparer VMT + FEqualityComparer_Int8_VMT : TEqualityComparerVMT; + FEqualityComparer_Int16_VMT : TEqualityComparerVMT; + FEqualityComparer_Int32_VMT : TEqualityComparerVMT; + FEqualityComparer_Int64_VMT : TEqualityComparerVMT; + FEqualityComparer_UInt8_VMT : TEqualityComparerVMT; + FEqualityComparer_UInt16_VMT: TEqualityComparerVMT; + FEqualityComparer_UInt32_VMT: TEqualityComparerVMT; + FEqualityComparer_UInt64_VMT: TEqualityComparerVMT; + + FEqualityComparer_Single_VMT : TEqualityComparerVMT; + FEqualityComparer_Double_VMT : TEqualityComparerVMT; + FEqualityComparer_Extended_VMT: TEqualityComparerVMT; + + FEqualityComparer_Currency_VMT: TEqualityComparerVMT; + FEqualityComparer_Comp_VMT : TEqualityComparerVMT; + + FEqualityComparer_Binary_VMT : TEqualityComparerVMT; + FEqualityComparer_DynArray_VMT: TEqualityComparerVMT; + + FEqualityComparer_Class_VMT: TEqualityComparerVMT; + + FEqualityComparer_ShortString1_VMT : TEqualityComparerVMT; + FEqualityComparer_ShortString2_VMT : TEqualityComparerVMT; + FEqualityComparer_ShortString3_VMT : TEqualityComparerVMT; + FEqualityComparer_ShortString_VMT : TEqualityComparerVMT; + FEqualityComparer_AnsiString_VMT : TEqualityComparerVMT; + FEqualityComparer_WideString_VMT : TEqualityComparerVMT; + FEqualityComparer_UnicodeString_VMT: TEqualityComparerVMT; + + FEqualityComparer_Method_VMT : TEqualityComparerVMT; + FEqualityComparer_Variant_VMT: TEqualityComparerVMT; + FEqualityComparer_Pointer_VMT: TEqualityComparerVMT; + + FEqualityComparer_Int8_Instance : Pointer; + FEqualityComparer_Int16_Instance : Pointer; + FEqualityComparer_Int32_Instance : Pointer; + FEqualityComparer_Int64_Instance : Pointer; + FEqualityComparer_UInt8_Instance : Pointer; + FEqualityComparer_UInt16_Instance : Pointer; + FEqualityComparer_UInt32_Instance : Pointer; + FEqualityComparer_UInt64_Instance : Pointer; + + FEqualityComparer_Single_Instance : Pointer; + FEqualityComparer_Double_Instance : Pointer; + FEqualityComparer_Extended_Instance : Pointer; + + FEqualityComparer_Currency_Instance : Pointer; + FEqualityComparer_Comp_Instance : Pointer; + + //FEqualityComparer_Binary_Instance : Pointer; // dynamic instance + //FEqualityComparer_DynArray_Instance : Pointer; // dynamic instance + + FEqualityComparer_ShortString1_Instance : Pointer; + FEqualityComparer_ShortString2_Instance : Pointer; + FEqualityComparer_ShortString3_Instance : Pointer; + FEqualityComparer_ShortString_Instance : Pointer; + FEqualityComparer_AnsiString_Instance : Pointer; + FEqualityComparer_WideString_Instance : Pointer; + FEqualityComparer_UnicodeString_Instance: Pointer; + + FEqualityComparer_Method_Instance : Pointer; + FEqualityComparer_Variant_Instance : Pointer; + FEqualityComparer_Pointer_Instance : Pointer; + + + FEqualityComparerInstances: array[TTypeKind] of TInstance; + public + function LookupEqualityComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; override; + + constructor Create; override; + + //class function Hash(AKey: Pointer; ASize: SizeInt): UInt32; virtual; overload; abstract; + //class function Hash(AKey: Pointer; ASize: SizeInt; out AHash: UInt32): UInt32; virtual; overload; + end; + + { TExtendedHashFactory } + + TExtendedHashFactory = class(TExtendedHashCode) + private + function SelectIntegerEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override; + function SelectFloatEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override; + function SelectShortStringEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override; + function SelectBinaryEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override; + function SelectDynArrayEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; override; + private const + // IExtendedEqualityComparer VMT templates +{$WARNINGS OFF} + ExtendedEqualityComparer_Int8_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int8 ; GetHashCode: @THashCode.Int8 ; GetHashList: @TExtendedHashCode.Int8 ); + ExtendedEqualityComparer_Int16_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int16 ; GetHashCode: @THashCode.Int16 ; GetHashList: @TExtendedHashCode.Int16 ); + ExtendedEqualityComparer_Int32_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int32 ; GetHashCode: @THashCode.Int32 ; GetHashList: @TExtendedHashCode.Int32 ); + ExtendedEqualityComparer_Int64_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Int64 ; GetHashCode: @THashCode.Int64 ; GetHashList: @TExtendedHashCode.Int64 ); + ExtendedEqualityComparer_UInt8_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt8 ; GetHashCode: @THashCode.UInt8 ; GetHashList: @TExtendedHashCode.UInt8 ); + ExtendedEqualityComparer_UInt16_VMT: TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt16; GetHashCode: @THashCode.UInt16; GetHashList: @TExtendedHashCode.UInt16); + ExtendedEqualityComparer_UInt32_VMT: TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt32; GetHashCode: @THashCode.UInt32; GetHashList: @TExtendedHashCode.UInt32); + ExtendedEqualityComparer_UInt64_VMT: TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UInt64; GetHashCode: @THashCode.UInt64; GetHashList: @TExtendedHashCode.UInt64); + + ExtendedEqualityComparer_Single_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Single ; GetHashCode: @THashCode.Single ; GetHashList: @TExtendedHashCode.Single ); + ExtendedEqualityComparer_Double_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Double ; GetHashCode: @THashCode.Double ; GetHashList: @TExtendedHashCode.Double ); + ExtendedEqualityComparer_Extended_VMT: TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Extended; GetHashCode: @THashCode.Extended; GetHashList: @TExtendedHashCode.Extended); + + ExtendedEqualityComparer_Currency_VMT: TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Currency; GetHashCode: @THashCode.Currency; GetHashList: @TExtendedHashCode.Currency); + ExtendedEqualityComparer_Comp_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Comp ; GetHashCode: @THashCode.Comp ; GetHashList: @TExtendedHashCode.Comp ); + + ExtendedEqualityComparer_Binary_VMT : TExtendedEqualityComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Equals: @TEquals._Binary ; GetHashCode: @THashCode.Binary ; GetHashList: @TExtendedHashCode.Binary ); + ExtendedEqualityComparer_DynArray_VMT: TExtendedEqualityComparerVMT = (STD_COM_TYPESIZE_INTERFACE_METHODS; Equals: @TEquals._DynArray; GetHashCode: @THashCode.DynArray; GetHashList: @TExtendedHashCode.DynArray); + + ExtendedEqualityComparer_Class_VMT: TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.&Class; GetHashCode: @THashCode.&Class; GetHashList: @TExtendedHashCode.&Class); + + ExtendedEqualityComparer_ShortString1_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString1 ; GetHashCode: @THashCode.ShortString1 ; GetHashList: @TExtendedHashCode.ShortString1 ); + ExtendedEqualityComparer_ShortString2_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString2 ; GetHashCode: @THashCode.ShortString2 ; GetHashList: @TExtendedHashCode.ShortString2 ); + ExtendedEqualityComparer_ShortString3_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString3 ; GetHashCode: @THashCode.ShortString3 ; GetHashList: @TExtendedHashCode.ShortString3 ); + ExtendedEqualityComparer_ShortString_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.ShortString ; GetHashCode: @THashCode.ShortString ; GetHashList: @TExtendedHashCode.ShortString ); + ExtendedEqualityComparer_AnsiString_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.AnsiString ; GetHashCode: @THashCode.AnsiString ; GetHashList: @TExtendedHashCode.AnsiString ); + ExtendedEqualityComparer_WideString_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.WideString ; GetHashCode: @THashCode.WideString ; GetHashList: @TExtendedHashCode.WideString ); + ExtendedEqualityComparer_UnicodeString_VMT: TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.UnicodeString; GetHashCode: @THashCode.UnicodeString; GetHashList: @TExtendedHashCode.UnicodeString); + + ExtendedEqualityComparer_Method_VMT : TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Method ; GetHashCode: @THashCode.Method ; GetHashList: @TExtendedHashCode.Method ); + ExtendedEqualityComparer_Variant_VMT: TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Variant; GetHashCode: @THashCode.Variant; GetHashList: @TExtendedHashCode.Variant); + ExtendedEqualityComparer_Pointer_VMT: TExtendedEqualityComparerVMT = (STD_RAW_INTERFACE_METHODS; Equals: @TEquals.Pointer; GetHashCode: @THashCode.Pointer; GetHashList: @TExtendedHashCode.Pointer); +{$WARNINGS ON} + private var + // IExtendedEqualityComparer VMT + FExtendedEqualityComparer_Int8_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_Int16_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_Int32_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_Int64_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_UInt8_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_UInt16_VMT: TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_UInt32_VMT: TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_UInt64_VMT: TExtendedEqualityComparerVMT; + + FExtendedEqualityComparer_Single_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_Double_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_Extended_VMT: TExtendedEqualityComparerVMT; + + FExtendedEqualityComparer_Currency_VMT: TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_Comp_VMT : TExtendedEqualityComparerVMT; + + FExtendedEqualityComparer_Binary_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_DynArray_VMT: TExtendedEqualityComparerVMT; + + FExtendedEqualityComparer_Class_VMT: TExtendedEqualityComparerVMT; + + FExtendedEqualityComparer_ShortString1_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_ShortString2_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_ShortString3_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_ShortString_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_AnsiString_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_WideString_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_UnicodeString_VMT: TExtendedEqualityComparerVMT; + + FExtendedEqualityComparer_Method_VMT : TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_Variant_VMT: TExtendedEqualityComparerVMT; + FExtendedEqualityComparer_Pointer_VMT: TExtendedEqualityComparerVMT; + + FExtendedEqualityComparer_Int8_Instance : Pointer; + FExtendedEqualityComparer_Int16_Instance : Pointer; + FExtendedEqualityComparer_Int32_Instance : Pointer; + FExtendedEqualityComparer_Int64_Instance : Pointer; + FExtendedEqualityComparer_UInt8_Instance : Pointer; + FExtendedEqualityComparer_UInt16_Instance : Pointer; + FExtendedEqualityComparer_UInt32_Instance : Pointer; + FExtendedEqualityComparer_UInt64_Instance : Pointer; + + FExtendedEqualityComparer_Single_Instance : Pointer; + FExtendedEqualityComparer_Double_Instance : Pointer; + FExtendedEqualityComparer_Extended_Instance : Pointer; + + FExtendedEqualityComparer_Currency_Instance : Pointer; + FExtendedEqualityComparer_Comp_Instance : Pointer; + + //FExtendedEqualityComparer_Binary_Instance : Pointer; // dynamic instance + //FExtendedEqualityComparer_DynArray_Instance : Pointer; // dynamic instance + + FExtendedEqualityComparer_ShortString1_Instance : Pointer; + FExtendedEqualityComparer_ShortString2_Instance : Pointer; + FExtendedEqualityComparer_ShortString3_Instance : Pointer; + FExtendedEqualityComparer_ShortString_Instance : Pointer; + FExtendedEqualityComparer_AnsiString_Instance : Pointer; + FExtendedEqualityComparer_WideString_Instance : Pointer; + FExtendedEqualityComparer_UnicodeString_Instance: Pointer; + + FExtendedEqualityComparer_Method_Instance : Pointer; + FExtendedEqualityComparer_Variant_Instance : Pointer; + FExtendedEqualityComparer_Pointer_Instance : Pointer; + + // all instances + FExtendedEqualityComparerInstances: array[TTypeKind] of TInstance; + public + function LookupExtendedEqualityComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; override; + + constructor Create; override; + end; + + TOnEqualityComparison<T> = function(constref ALeft, ARight: T): Boolean of object; + TEqualityComparisonFunc<T> = function(constref ALeft, ARight: T): Boolean; + + TOnHasher<T> = function(constref AValue: T): UInt32 of object; + TOnExtendedHasher<T> = procedure(constref AValue: T; AHashList: PUInt32) of object; + THasherFunc<T> = function(constref AValue: T): UInt32; + TExtendedHasherFunc<T> = procedure(constref AValue: T; AHashList: PUInt32); + + TEqualityComparer<T> = class(TInterfacedObject, IEqualityComparer<T>) + public + class function Default: IEqualityComparer<T>; static; overload; + class function Default(AHashFactoryClass: TComparerFactoryClass): IEqualityComparer<T>; static; overload; + + class function Construct(const AEqualityComparison: TOnEqualityComparison<T>; + const AHasher: TOnHasher<T>): IEqualityComparer<T>; overload; + class function Construct(const AEqualityComparison: TEqualityComparisonFunc<T>; + const AHasher: THasherFunc<T>): IEqualityComparer<T>; overload; + + function Equals(constref ALeft, ARight: T): Boolean; virtual; overload; abstract; + function GetHashCode(constref AValue: T): UInt32; virtual; overload; abstract; + end; + + { TDelegatedEqualityComparerEvent } + + TDelegatedEqualityComparerEvents<T> = class(TEqualityComparer<T>) + private + FEqualityComparison: TOnEqualityComparison<T>; + FHasher: TOnHasher<T>; + public + function Equals(constref ALeft, ARight: T): Boolean; override; + function GetHashCode(constref AValue: T): UInt32; override; + + constructor Create(const AEqualityComparison: TOnEqualityComparison<T>; + const AHasher: TOnHasher<T>); + end; + + TDelegatedEqualityComparerFunc<T> = class(TEqualityComparer<T>) + private + FEqualityComparison: TEqualityComparisonFunc<T>; + FHasher: THasherFunc<T>; + public + function Equals(constref ALeft, ARight: T): Boolean; override; + function GetHashCode(constref AValue: T): UInt32; override; + + constructor Create(const AEqualityComparison: TEqualityComparisonFunc<T>; + const AHasher: THasherFunc<T>); + end; + + { TExtendedEqualityComparer } + + TExtendedEqualityComparer<T> = class(TEqualityComparer<T>, IExtendedEqualityComparer<T>) + public + class function Default: IExtendedEqualityComparer<T>; static; overload; reintroduce; + class function Default(AExtenedHashFactoryClass: TExtendedHashFactoryClass): IExtendedEqualityComparer<T>; static; overload; reintroduce; + + class function Construct(const AEqualityComparison: TOnEqualityComparison<T>; + const AHasher: TOnHasher<T>; const AExtendedHasher: TOnExtendedHasher<T>): IExtendedEqualityComparer<T>; overload; reintroduce; + class function Construct(const AEqualityComparison: TEqualityComparisonFunc<T>; + const AHasher: THasherFunc<T>; const AExtendedHasher: TExtendedHasherFunc<T>): IExtendedEqualityComparer<T>; overload; reintroduce; + class function Construct(const AEqualityComparison: TOnEqualityComparison<T>; + const AExtendedHasher: TOnExtendedHasher<T>): IExtendedEqualityComparer<T>; overload; reintroduce; + class function Construct(const AEqualityComparison: TEqualityComparisonFunc<T>; + const AExtendedHasher: TExtendedHasherFunc<T>): IExtendedEqualityComparer<T>; overload; reintroduce; + + procedure GetHashList(constref AValue: T; AHashList: PUInt32); virtual; abstract; + end; + + TDelegatedExtendedEqualityComparerEvents<T> = class(TExtendedEqualityComparer<T>) + private + FEqualityComparison: TOnEqualityComparison<T>; + FHasher: TOnHasher<T>; + FExtendedHasher: TOnExtendedHasher<T>; + + function GetHashCodeMethod(constref AValue: T): UInt32; + public + function Equals(constref ALeft, ARight: T): Boolean; override; + function GetHashCode(constref AValue: T): UInt32; override; + procedure GetHashList(constref AValue: T; AHashList: PUInt32); override; + + constructor Create(const AEqualityComparison: TOnEqualityComparison<T>; + const AHasher: TOnHasher<T>; const AExtendedHasher: TOnExtendedHasher<T>); overload; + constructor Create(const AEqualityComparison: TOnEqualityComparison<T>; + const AExtendedHasher: TOnExtendedHasher<T>); overload; + end; + + TDelegatedExtendedEqualityComparerFunc<T> = class(TExtendedEqualityComparer<T>) + private + FEqualityComparison: TEqualityComparisonFunc<T>; + FHasher: THasherFunc<T>; + FExtendedHasher: TExtendedHasherFunc<T>; + public + function Equals(constref ALeft, ARight: T): Boolean; override; + function GetHashCode(constref AValue: T): UInt32; override; + procedure GetHashList(constref AValue: T; AHashList: PUInt32); override; + + constructor Create(const AEqualityComparison: TEqualityComparisonFunc<T>; + const AHasher: THasherFunc<T>; const AExtendedHasher: TExtendedHasherFunc<T>); overload; + constructor Create(const AEqualityComparison: TEqualityComparisonFunc<T>; + const AExtendedHasher: TExtendedHasherFunc<T>); overload; + end; + + { TDelphiHashFactory } + + TDelphiHashFactory = class(THashFactory) + strict private class var + FID: Integer; + class constructor Create; + protected + class function GetID: Integer; override; + public + class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override; + end; + + { TAdler32HashFactory } + + TAdler32HashFactory = class(THashFactory) + strict private class var + FID: Integer; + class constructor Create; + protected + class function GetID: Integer; override; + public + class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override; + end; + + { TSdbmHashFactory } + + TSdbmHashFactory = class(THashFactory) + strict private class var + FID: Integer; + class constructor Create; + protected + class function GetID: Integer; override; + public + class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override; + end; + + { TSdbmHashFactory } + + TSimpleChecksumFactory = class(THashFactory) + strict private class var + FID: Integer; + class constructor Create; + protected + class function GetID: Integer; override; + public + class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override; + end; + + { TDelphiDoubleHashFactory } + + TDelphiDoubleHashFactory = class(TExtendedHashFactory) + strict private class var + FID: Integer; + + class constructor Create; + protected + class function GetID: Integer; override; + public + const MAX_HASHLIST_COUNT = 2; + const HASH_FUNCTIONS_COUNT = 1; + const HASHLIST_COUNT_PER_FUNCTION: array[1..HASH_FUNCTIONS_COUNT] of Integer = (2); + const HASH_FUNCTIONS_MASK_SIZE = 1; + const HASH_FUNCTIONS_MASK = 1; // 00000001b + + class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override; + class procedure GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; AOptions: TGetHashListOptions = []); override; + end; + + TDelphiQuadrupleHashFactory = class(TExtendedHashFactory) + strict private class var + FID: Integer; + + class constructor Create; + protected + class function GetID: Integer; override; + public + const MAX_HASHLIST_COUNT = 4; + const HASH_FUNCTIONS_COUNT = 2; + const HASHLIST_COUNT_PER_FUNCTION: array[1..HASH_FUNCTIONS_COUNT] of Integer = (2, 2); + const HASH_FUNCTIONS_MASK_SIZE = 2; + const HASH_FUNCTIONS_MASK = 3; // 00000011b + + class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override; + class procedure GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; AOptions: TGetHashListOptions = []); override; + end; + + TDelphiSixfoldHashFactory = class(TExtendedHashFactory) + strict private class var + FID: Integer; + + class constructor Create; + protected + class function GetID: Integer; override; + public + const MAX_HASHLIST_COUNT = 6; + const HASH_FUNCTIONS_COUNT = 3; + const HASHLIST_COUNT_PER_FUNCTION: array[1..HASH_FUNCTIONS_COUNT] of Integer = (2, 2, 2); + const HASH_FUNCTIONS_MASK_SIZE = 3; + const HASH_FUNCTIONS_MASK = 7; // 00000111b + + class function GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32 = 0): UInt32; override; + class procedure GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; AOptions: TGetHashListOptions = []); override; + end; + + TDefaultHashFactory = TDelphiQuadrupleHashFactory; + + TDefaultGenericInterface = (giComparer, giEqualityComparer, giExtendedEqualityComparer); + + TCustomComparer<T> = class(TSingletonImplementation, IComparer<T>, IEqualityComparer<T>, IExtendedEqualityComparer<T>) + protected + function Compare(constref Left, Right: T): Integer; virtual; abstract; + function Equals(constref Left, Right: T): Boolean; reintroduce; overload; virtual; abstract; + function GetHashCode(constref Value: T): UInt32; reintroduce; overload; virtual; abstract; + procedure GetHashList(constref Value: T; AHashList: PUInt32); virtual; abstract; + end; + + TOrdinalComparer<T, THashFactory> = class(TCustomComparer<T>) + protected class var + FComparer: IComparer<T>; + FEqualityComparer: IEqualityComparer<T>; + FExtendedEqualityComparer: IExtendedEqualityComparer<T>; + + class constructor Create; + public + class function Ordinal: TCustomComparer<T>; virtual; abstract; + end; + + // TGStringComparer will be renamed to TStringComparer -> bug #26030 + // anyway class var can't be used safely -> bug #24848 + + TGStringComparer<T, THashFactory> = class(TOrdinalComparer<T, THashFactory>) + private class var + FOrdinal: TCustomComparer<T>; + class destructor Destroy; + public + class function Ordinal: TCustomComparer<T>; override; + end; + + TGStringComparer<T> = class(TGStringComparer<T, TDelphiQuadrupleHashFactory>); + TStringComparer = class(TGStringComparer<string>); + TAnsiStringComparer = class(TGStringComparer<AnsiString>); + TUnicodeStringComparer = class(TGStringComparer<UnicodeString>); + + { TGOrdinalStringComparer } + + // TGOrdinalStringComparer will be renamed to TOrdinalStringComparer -> bug #26030 + // anyway class var can't be used safely -> bug #24848 + TGOrdinalStringComparer<T, THashFactory> = class(TGStringComparer<T, THashFactory>) + public + function Compare(constref ALeft, ARight: T): Integer; override; + function Equals(constref ALeft, ARight: T): Boolean; overload; override; + function GetHashCode(constref AValue: T): UInt32; overload; override; + procedure GetHashList(constref AValue: T; AHashList: PUInt32); override; + end; + + TGOrdinalStringComparer<T> = class(TGOrdinalStringComparer<T, TDelphiQuadrupleHashFactory>); + TOrdinalStringComparer = class(TGOrdinalStringComparer<string>); + + TGIStringComparer<T, THashFactory> = class(TOrdinalComparer<T, THashFactory>) + private class var + FOrdinal: TCustomComparer<T>; + class destructor Destroy; + public + class function Ordinal: TCustomComparer<T>; override; + end; + + TGIStringComparer<T> = class(TGIStringComparer<T, TDelphiQuadrupleHashFactory>); + TIStringComparer = class(TGIStringComparer<string>); + TIAnsiStringComparer = class(TGIStringComparer<AnsiString>); + TIUnicodeStringComparer = class(TGIStringComparer<UnicodeString>); + + TGOrdinalIStringComparer<T, THashFactory> = class(TGIStringComparer<T, THashFactory>) + public + function Compare(constref ALeft, ARight: T): Integer; override; + function Equals(constref ALeft, ARight: T): Boolean; overload; override; + function GetHashCode(constref AValue: T): UInt32; overload; override; + procedure GetHashList(constref AValue: T; AHashList: PUInt32); override; + end; + + TGOrdinalIStringComparer<T> = class(TGOrdinalIStringComparer<T, TDelphiQuadrupleHashFactory>); + TOrdinalIStringComparer = class(TGOrdinalIStringComparer<string>); + +// Delphi version of Bob Jenkins Hash +function BobJenkinsHash(const AData; ALength, AInitData: Integer): Integer; // same result as HashLittle_Delphi, just different interface +function BinaryCompare(const ALeft, ARight: Pointer; ASize: PtrUInt): Integer; inline; + +function _LookupVtableInfo(AGInterface: TDefaultGenericInterface; ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; inline; +function _LookupVtableInfoEx(AGInterface: TDefaultGenericInterface; ATypeInfo: PTypeInfo; ASize: SizeInt; + AFactory: TComparerFactoryClass): Pointer; + +implementation + +var + ComparerFactory: array of TComparerFactory; + +{ TComparer<T> } + +class function TComparer<T>.Default: IComparer<T>; +begin + Result := _LookupVtableInfo(giComparer, TypeInfo(T), SizeOf(T)); +end; + +class function TComparer<T>.Construct(const AComparison: TOnComparison<T>): IComparer<T>; +begin + Result := TDelegatedComparerEvents<T>.Create(AComparison); +end; + +class function TComparer<T>.Construct(const AComparison: TComparisonFunc<T>): IComparer<T>; +begin + Result := TDelegatedComparerFunc<T>.Create(AComparison); +end; + +function TDelegatedComparerEvents<T>.Compare(constref ALeft, ARight: T): Integer; +begin + Result := FComparison(ALeft, ARight); +end; + +constructor TDelegatedComparerEvents<T>.Create(AComparison: TOnComparison<T>); +begin + FComparison := AComparison; +end; + +function TDelegatedComparerFunc<T>.Compare(constref ALeft, ARight: T): Integer; +begin + Result := FComparison(ALeft, ARight); +end; + +constructor TDelegatedComparerFunc<T>.Create(AComparison: TComparisonFunc<T>); +begin + FComparison := AComparison; +end; + +{ TInterface } + +function TInterface.QueryInterface(constref IID: TGUID; out Obj): HResult; +begin + Result := E_NOINTERFACE; +end; + +{ TRawInterface } + +function TRawInterface._AddRef: Integer; +begin + Result := -1; +end; + +function TRawInterface._Release: Integer; +begin + Result := -1; +end; + +{ TComTypeSizeInterface } + +function TComTypeSizeInterface._AddRef: Integer; +var + _self: TComparerFactory.PSpoofInterfacedTypeSizeObject absolute Self; +begin + Result := InterLockedIncrement(_self.RefCount); +end; + +function TComTypeSizeInterface._Release: Integer; +var + _self: TComparerFactory.PSpoofInterfacedTypeSizeObject absolute Self; +begin + Result := InterLockedDecrement(_self.RefCount); + if _self.RefCount = 0 then + Dispose(_self); +end; + +{ TSingletonImplementation } + +function TSingletonImplementation.QueryInterface(constref IID: TGUID; out Obj): HResult; +begin + if GetInterface(IID, Obj) then + Result := S_OK + else + Result := E_NOINTERFACE; +end; + +{ TCompare } + +(*********************************************************************************************************************** + Comparers +(**********************************************************************************************************************) + +{----------------------------------------------------------------------------------------------------------------------- + Comparers Int8 - Int32 and UInt8 - UInt32 +{----------------------------------------------------------------------------------------------------------------------} + +class function TCompare.Integer(constref ALeft, ARight: Integer): Integer; +begin + Result := Math.CompareValue(ALeft, ARight); +end; + +class function TCompare.Int8(constref ALeft, ARight: Int8): Integer; +begin + Result := ALeft - ARight; +end; + +class function TCompare.Int16(constref ALeft, ARight: Int16): Integer; +begin + Result := ALeft - ARight; +end; + +class function TCompare.Int32(constref ALeft, ARight: Int32): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +class function TCompare.Int64(constref ALeft, ARight: Int64): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +class function TCompare.UInt8(constref ALeft, ARight: UInt8): Integer; +begin + Result := System.Integer(ALeft) - System.Integer(ARight); +end; + +class function TCompare.UInt16(constref ALeft, ARight: UInt16): Integer; +begin + Result := System.Integer(ALeft) - System.Integer(ARight); +end; + +class function TCompare.UInt32(constref ALeft, ARight: UInt32): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +class function TCompare.UInt64(constref ALeft, ARight: UInt64): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +{----------------------------------------------------------------------------------------------------------------------- + Comparers for Float types +{----------------------------------------------------------------------------------------------------------------------} + +class function TCompare.Single(constref ALeft, ARight: Single): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +class function TCompare.Double(constref ALeft, ARight: Double): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +class function TCompare.Extended(constref ALeft, ARight: Extended): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +{----------------------------------------------------------------------------------------------------------------------- + Comparers for other number types +{----------------------------------------------------------------------------------------------------------------------} + +class function TCompare.Currency(constref ALeft, ARight: Currency): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +class function TCompare.Comp(constref ALeft, ARight: Comp): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +{----------------------------------------------------------------------------------------------------------------------- + Comparers for binary data (records etc) and dynamics arrays +{----------------------------------------------------------------------------------------------------------------------} + +class function TCompare._Binary(constref ALeft, ARight): Integer; +var + _self: TComparerFactory.PSpoofInterfacedTypeSizeObject absolute Self; +begin + Result := CompareMemRange(@ALeft, @ARight, _self.Size); +end; + +class function TCompare._DynArray(constref ALeft, ARight: Pointer): Integer; +var + _self: TComparerFactory.PSpoofInterfacedTypeSizeObject absolute Self; + LLength, LLeftLength, LRightLength: Integer; +begin + LLeftLength := DynArraySize(ALeft); + LRightLength := DynArraySize(ARight); + if LLeftLength > LRightLength then + LLength := LRightLength + else + LLength := LLeftLength; + + Result := CompareMemRange(ALeft, ARight, LLength * _self.Size); + + if Result = 0 then + Result := LLeftLength - LRightLength; +end; + +class function TCompare.Binary(constref ALeft, ARight; const ASize: SizeInt): Integer; +begin + Result := CompareMemRange(@ALeft, @ARight, ASize); +end; + +class function TCompare.DynArray(constref ALeft, ARight: Pointer; const AElementSize: SizeInt): Integer; +var + LLength, LLeftLength, LRightLength: Integer; +begin + LLeftLength := DynArraySize(ALeft); + LRightLength := DynArraySize(ARight); + if LLeftLength > LRightLength then + LLength := LRightLength + else + LLength := LLeftLength; + + Result := CompareMemRange(ALeft, ARight, LLength * AElementSize); + + if Result = 0 then + Result := LLeftLength - LRightLength; +end; + +{----------------------------------------------------------------------------------------------------------------------- + Comparers for string types +{----------------------------------------------------------------------------------------------------------------------} + +class function TCompare.ShortString1(constref ALeft, ARight: ShortString1): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +class function TCompare.ShortString2(constref ALeft, ARight: ShortString2): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +class function TCompare.ShortString3(constref ALeft, ARight: ShortString3): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +class function TCompare.ShortString(constref ALeft, ARight: OpenString): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +class function TCompare.&String(constref ALeft, ARight: String): Integer; +begin + Result := CompareStr(ALeft, ARight); +end; + +class function TCompare.AnsiString(constref ALeft, ARight: AnsiString): Integer; +begin + Result := AnsiCompareStr(ALeft, ARight); +end; + +class function TCompare.WideString(constref ALeft, ARight: WideString): Integer; +begin + Result := WideCompareStr(ALeft, ARight); +end; + +class function TCompare.UnicodeString(constref ALeft, ARight: UnicodeString): Integer; +begin + Result := UnicodeCompareStr(ALeft, ARight); +end; + +{----------------------------------------------------------------------------------------------------------------------- + Comparers for Delegates +{----------------------------------------------------------------------------------------------------------------------} + +class function TCompare.Method(constref ALeft, ARight: TMethod): Integer; +begin + Result := CompareMemRange(@ALeft, @ARight, SizeOf(System.TMethod)); +end; + +{----------------------------------------------------------------------------------------------------------------------- + Comparers for Variant +{----------------------------------------------------------------------------------------------------------------------} + +class function TCompare.Variant(constref ALeft, ARight: PVariant): Integer; +var + LLeftString, LRightString: string; +begin + try + case VarCompareValue(ALeft^, ARight^) of + vrGreaterThan: + Exit(1); + vrLessThan: + Exit(-1); + vrEqual: + Exit(0); + vrNotEqual: + if VarIsEmpty(ALeft^) or VarIsNull(ALeft^) then + Exit(1) + else + Exit(-1); + end; + except + try + LLeftString := ALeft^; + LRightString := ARight^; + Result := CompareStr(LLeftString, LRightString); + except + Result := CompareMemRange(ALeft, ARight, SizeOf(System.Variant)); + end; + end; +end; + +{----------------------------------------------------------------------------------------------------------------------- + Comparers for Pointer +{----------------------------------------------------------------------------------------------------------------------} + +class function TCompare.Pointer(constref ALeft, ARight: PtrUInt): Integer; +begin + if ALeft > ARight then + Exit(1) + else if ALeft < ARight then + Exit(-1) + else + Exit(0); +end; + +{ TEquals } + +(*********************************************************************************************************************** + Equality Comparers +(**********************************************************************************************************************) + +{----------------------------------------------------------------------------------------------------------------------- + Equality Comparers Int8 - Int32 and UInt8 - UInt32 +{----------------------------------------------------------------------------------------------------------------------} + +class function TEquals.Integer(constref ALeft, ARight: Integer): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.Int8(constref ALeft, ARight: Int8): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.Int16(constref ALeft, ARight: Int16): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.Int32(constref ALeft, ARight: Int32): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.Int64(constref ALeft, ARight: Int64): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.UInt8(constref ALeft, ARight: UInt8): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.UInt16(constref ALeft, ARight: UInt16): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.UInt32(constref ALeft, ARight: UInt32): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.UInt64(constref ALeft, ARight: UInt64): Boolean; +begin + Result := ALeft = ARight; +end; + +{----------------------------------------------------------------------------------------------------------------------- + Equality Comparers for Float types +{----------------------------------------------------------------------------------------------------------------------} + +class function TEquals.Single(constref ALeft, ARight: Single): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.Double(constref ALeft, ARight: Double): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.Extended(constref ALeft, ARight: Extended): Boolean; +begin + Result := ALeft = ARight; +end; + +{----------------------------------------------------------------------------------------------------------------------- + Equality Comparers for other number types +{----------------------------------------------------------------------------------------------------------------------} + +class function TEquals.Currency(constref ALeft, ARight: Currency): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.Comp(constref ALeft, ARight: Comp): Boolean; +begin + Result := ALeft = ARight; +end; + +{----------------------------------------------------------------------------------------------------------------------- + Equality Comparers for binary data (records etc) and dynamics arrays +{----------------------------------------------------------------------------------------------------------------------} + +class function TEquals._Binary(constref ALeft, ARight): Boolean; +var + _self: TComparerFactory.PSpoofInterfacedTypeSizeObject absolute Self; +begin + Result := CompareMem(@ALeft, @ARight, _self.Size); +end; + +class function TEquals._DynArray(constref ALeft, ARight: Pointer): Boolean; +var + _self: TComparerFactory.PSpoofInterfacedTypeSizeObject absolute Self; + LLength: Integer; +begin + LLength := DynArraySize(ALeft); + if LLength <> DynArraySize(ARight) then + Exit(False); + + Result := CompareMem(ALeft, ARight, LLength * _self.Size); +end; + +class function TEquals.Binary(constref ALeft, ARight; const ASize: SizeInt): Boolean; +begin + Result := CompareMem(@ALeft, @ARight, ASize); +end; + +class function TEquals.DynArray(constref ALeft, ARight: Pointer; const AElementSize: SizeInt): Boolean; +var + LLength: Integer; +begin + LLength := DynArraySize(ALeft); + if LLength <> DynArraySize(ARight) then + Exit(False); + + Result := CompareMem(ALeft, ARight, LLength * AElementSize); +end; + +{----------------------------------------------------------------------------------------------------------------------- + Equality Comparers for classes +{----------------------------------------------------------------------------------------------------------------------} + +class function TEquals.&class(constref ALeft, ARight: TObject): Boolean; +begin + if ALeft <> nil then + Exit(ALeft.Equals(ARight)) + else + Exit(ARight = nil); +end; + +{----------------------------------------------------------------------------------------------------------------------- + Equality Comparers for string types +{----------------------------------------------------------------------------------------------------------------------} + +class function TEquals.ShortString1(constref ALeft, ARight: ShortString1): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.ShortString2(constref ALeft, ARight: ShortString2): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.ShortString3(constref ALeft, ARight: ShortString3): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.&String(constref ALeft, ARight: String): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.ShortString(constref ALeft, ARight: OpenString): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.AnsiString(constref ALeft, ARight: AnsiString): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.WideString(constref ALeft, ARight: WideString): Boolean; +begin + Result := ALeft = ARight; +end; + +class function TEquals.UnicodeString(constref ALeft, ARight: UnicodeString): Boolean; +begin + Result := ALeft = ARight; +end; + +{----------------------------------------------------------------------------------------------------------------------- + Equality Comparers for Delegates +{----------------------------------------------------------------------------------------------------------------------} + +class function TEquals.Method(constref ALeft, ARight: TMethod): Boolean; +begin + Result := (ALeft.Code = ARight.Code) and (ALeft.Data = ARight.Data); +end; + +{----------------------------------------------------------------------------------------------------------------------- + Equality Comparers for Variant +{----------------------------------------------------------------------------------------------------------------------} + +class function TEquals.Variant(constref ALeft, ARight: PVariant): Boolean; +begin + Result := VarCompareValue(ALeft^, ARight^) = vrEqual; +end; + +{----------------------------------------------------------------------------------------------------------------------- + Equality Comparers for Pointer +{----------------------------------------------------------------------------------------------------------------------} + +class function TEquals.Pointer(constref ALeft, ARight: PtrUInt): Boolean; +begin + Result := ALeft = ARight; +end; + +{ TComparerFactory } + +class function TComparerFactory.CreateInterface(AVMT: Pointer; ASize: SizeInt): PSpoofInterfacedTypeSizeObject; +begin + Result := New(PSpoofInterfacedTypeSizeObject); + Result.VMT := AVMT; + Result.RefCount := 0; + Result.Size := ASize; +end; + +class function TComparerFactory.SelectIntegerComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + case ATypeData.OrdType of + otSByte: + Exit(@Comparer_Int8_Instance); + otUByte: + Exit(@Comparer_UInt8_Instance); + otSWord: + Exit(@Comparer_Int16_Instance); + otUWord: + Exit(@Comparer_UInt16_Instance); + otSLong: + Exit(@Comparer_Int32_Instance); + otULong: + Exit(@Comparer_UInt32_Instance); + else + System.Error(reRangeError); + Exit(nil); + end; +end; + +class function TComparerFactory.SelectInt64Comparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + if ATypeData.MaxInt64Value > ATypeData.MinInt64Value then + Exit(@Comparer_Int64_Instance) + else + Exit(@Comparer_UInt64_Instance); +end; + +class function TComparerFactory.SelectFloatComparer(ATypeData: PTypeData; + ASize: SizeInt): Pointer; +begin + case ATypeData.FloatType of + ftSingle: + Exit(@Comparer_Single_Instance); + ftDouble: + Exit(@Comparer_Double_Instance); + ftExtended: + Exit(@Comparer_Extended_Instance); + ftComp: + Exit(@Comparer_Comp_Instance); + ftCurr: + Exit(@Comparer_Currency_Instance); + else + System.Error(reRangeError); + Exit(nil); + end; +end; + +class function TComparerFactory.SelectShortStringComparer(ATypeData: PTypeData; + ASize: SizeInt): Pointer; +begin + case ASize of + 2: Exit(@Comparer_ShortString1_Instance); + 3: Exit(@Comparer_ShortString2_Instance); + 4: Exit(@Comparer_ShortString3_Instance); + else + Exit(@Comparer_ShortString_Instance); + end; +end; + +class function TComparerFactory.SelectBinaryComparer(ATypeData: PTypeData; + ASize: SizeInt): Pointer; +begin + case ASize of + 1: Exit(@Comparer_UInt8_Instance); + 2: Exit(@Comparer_UInt16_Instance); + 4: Exit(@Comparer_UInt32_Instance); +{$IFDEF CPU64} + 8: Exit(@Comparer_UInt64_Instance) +{$ENDIF} + else + Result := CreateInterface(@Comparer_Binary_VMT, ASize); + end; +end; + +class function TComparerFactory.SelectDynArrayComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + Result := CreateInterface(@Comparer_DynArray_VMT, ATypeData.elSize); +end; + +constructor TComparerFactory.Create; +begin +end; + +class function TComparerFactory.LookupComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; +var + LInstance: PInstance; +begin + if ATypeInfo = nil then + Exit(SelectBinaryComparer(GetTypeData(ATypeInfo), ASize)) + else + begin + LInstance := @ComparerInstances[ATypeInfo.Kind]; + Result := LInstance.Instance; + if LInstance.Selector then + Result := TSelectFunc(Result)(GetTypeData(ATypeInfo), ASize); + end; +end; + +class function TComparerFactory.Register(const AComparerFactory: TComparerFactoryClass): Integer; +begin + Result := Length(ComparerFactory); + SetLength(ComparerFactory, Result + 1); + ComparerFactory[Result] := AComparerFactory.Create; +end; + +{ TComparerFactory.TInstance } + +class function TComparerFactory.TInstance.Create(ASelector: Boolean; + AInstance: Pointer): THashCode.TInstance; +begin + Result.Selector := ASelector; + Result.Instance := AInstance; +end; + +{ THashCode } + +(*********************************************************************************************************************** + Hashes +(**********************************************************************************************************************) + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode Int8 - Int32 and UInt8 - UInt32 +{----------------------------------------------------------------------------------------------------------------------} + +class function THashCode.Int8(constref AValue: Int8): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int8), 0); +end; + +class function THashCode.Int16(constref AValue: Int16): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int16), 0); +end; + +class function THashCode.Int32(constref AValue: Int32): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int32), 0); +end; + +class function THashCode.Int64(constref AValue: Int64): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int64), 0); +end; + +class function THashCode.UInt8(constref AValue: UInt8): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.UInt8), 0); +end; + +class function THashCode.UInt16(constref AValue: UInt16): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.UInt16), 0); +end; + +class function THashCode.UInt32(constref AValue: UInt32): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.UInt32), 0); +end; + +class function THashCode.UInt64(constref AValue: UInt64): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.UInt64), 0); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for Float types +{----------------------------------------------------------------------------------------------------------------------} + +class function THashCode.Single(constref AValue: Single): UInt32; +var + LMantissa: Float; + LExponent: Integer; +begin + Frexp(AValue, LMantissa, LExponent); + + if LMantissa = 0 then + LMantissa := Abs(LMantissa); + + Result := HASH_FACTORY.GetHashCode(@LMantissa, SizeOf(Math.Float), 0); + Result := HASH_FACTORY.GetHashCode(@LExponent, SizeOf(System.Integer), Result); +end; + +class function THashCode.Double(constref AValue: Double): UInt32; +var + LMantissa: Float; + LExponent: Integer; +begin + Frexp(AValue, LMantissa, LExponent); + + if LMantissa = 0 then + LMantissa := Abs(LMantissa); + + Result := HASH_FACTORY.GetHashCode(@LMantissa, SizeOf(Math.Float), 0); + Result := HASH_FACTORY.GetHashCode(@LExponent, SizeOf(System.Integer), Result); +end; + +class function THashCode.Extended(constref AValue: Extended): UInt32; +var + LMantissa: Float; + LExponent: Integer; +begin + Frexp(AValue, LMantissa, LExponent); + + if LMantissa = 0 then + LMantissa := Abs(LMantissa); + + Result := HASH_FACTORY.GetHashCode(@LMantissa, SizeOf(Math.Float), 0); + Result := HASH_FACTORY.GetHashCode(@LExponent, SizeOf(System.Integer), Result); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for other number types +{----------------------------------------------------------------------------------------------------------------------} + +class function THashCode.Currency(constref AValue: Currency): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int64), 0); +end; + +class function THashCode.Comp(constref AValue: Comp): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Int64), 0); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for binary data (records etc) and dynamics arrays +{----------------------------------------------------------------------------------------------------------------------} + +class function THashCode.Binary(constref AValue): UInt32; +var + _self: PSpoofInterfacedTypeSizeObject absolute Self; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, _self.Size, 0); +end; + +class function THashCode.DynArray(constref AValue: Pointer): UInt32; +var + _self: PSpoofInterfacedTypeSizeObject absolute Self; +begin + Result := HASH_FACTORY.GetHashCode(AValue, DynArraySize(AValue) * _self.Size, 0); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for classes +{----------------------------------------------------------------------------------------------------------------------} + +class function THashCode.&Class(constref AValue: TObject): UInt32; +begin + if AValue = nil then + Exit($2A); + + Result := AValue.GetHashCode; +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for string types +{----------------------------------------------------------------------------------------------------------------------} + +class function THashCode.ShortString1(constref AValue: ShortString1): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue), 0); +end; + +class function THashCode.ShortString2(constref AValue: ShortString2): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue), 0); +end; + +class function THashCode.ShortString3(constref AValue: ShortString3): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue), 0); +end; + +class function THashCode.ShortString(constref AValue: OpenString): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue), 0); +end; + +class function THashCode.AnsiString(constref AValue: AnsiString): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue) * SizeOf(System.AnsiChar), 0); +end; + +class function THashCode.WideString(constref AValue: WideString): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue) * SizeOf(System.WideChar), 0); +end; + +class function THashCode.UnicodeString(constref AValue: UnicodeString): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue[1], Length(AValue) * SizeOf(System.UnicodeChar), 0); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for Delegates +{----------------------------------------------------------------------------------------------------------------------} + +class function THashCode.Method(constref AValue: TMethod): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.TMethod), 0); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for Variant +{----------------------------------------------------------------------------------------------------------------------} + +class function THashCode.Variant(constref AValue: PVariant): UInt32; +begin + try + Result := HASH_FACTORY.UnicodeString(AValue^); + except + Result := HASH_FACTORY.GetHashCode(AValue, SizeOf(System.Variant), 0); + end; +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for Pointer +{----------------------------------------------------------------------------------------------------------------------} + +class function THashCode.Pointer(constref AValue: Pointer): UInt32; +begin + Result := HASH_FACTORY.GetHashCode(@AValue, SizeOf(System.Pointer), 0); +end; + +{ TExtendedHashCode } + +(*********************************************************************************************************************** + Hashes 2 +(**********************************************************************************************************************) + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode Int8 - Int32 and UInt8 - UInt32 +{----------------------------------------------------------------------------------------------------------------------} + +class procedure TExtendedHashCode.Int8(constref AValue: Int8; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int8), AHashList, []); +end; + +class procedure TExtendedHashCode.Int16(constref AValue: Int16; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int16), AHashList, []); +end; + +class procedure TExtendedHashCode.Int32(constref AValue: Int32; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int32), AHashList, []); +end; + +class procedure TExtendedHashCode.Int64(constref AValue: Int64; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int64), AHashList, []); +end; + +class procedure TExtendedHashCode.UInt8(constref AValue: UInt8; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.UInt8), AHashList, []); +end; + +class procedure TExtendedHashCode.UInt16(constref AValue: UInt16; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.UInt16), AHashList, []); +end; + +class procedure TExtendedHashCode.UInt32(constref AValue: UInt32; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.UInt32), AHashList, []); +end; + +class procedure TExtendedHashCode.UInt64(constref AValue: UInt64; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.UInt64), AHashList, []); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for Float types +{----------------------------------------------------------------------------------------------------------------------} + +class procedure TExtendedHashCode.Single(constref AValue: Single; AHashList: PUInt32); +var + LMantissa: Float; + LExponent: Integer; +begin + Frexp(AValue, LMantissa, LExponent); + + if LMantissa = 0 then + LMantissa := Abs(LMantissa); + + EXTENDED_HASH_FACTORY.GetHashList(@LMantissa, SizeOf(Math.Float), AHashList, []); + EXTENDED_HASH_FACTORY.GetHashList(@LExponent, SizeOf(System.Integer), AHashList, [ghloHashListAsInitData]); +end; + +class procedure TExtendedHashCode.Double(constref AValue: Double; AHashList: PUInt32); +var + LMantissa: Float; + LExponent: Integer; +begin + Frexp(AValue, LMantissa, LExponent); + + if LMantissa = 0 then + LMantissa := Abs(LMantissa); + + EXTENDED_HASH_FACTORY.GetHashList(@LMantissa, SizeOf(Math.Float), AHashList, []); + EXTENDED_HASH_FACTORY.GetHashList(@LExponent, SizeOf(System.Integer), AHashList, [ghloHashListAsInitData]); +end; + +class procedure TExtendedHashCode.Extended(constref AValue: Extended; AHashList: PUInt32); +var + LMantissa: Float; + LExponent: Integer; +begin + Frexp(AValue, LMantissa, LExponent); + + if LMantissa = 0 then + LMantissa := Abs(LMantissa); + + EXTENDED_HASH_FACTORY.GetHashList(@LMantissa, SizeOf(Math.Float), AHashList, []); + EXTENDED_HASH_FACTORY.GetHashList(@LExponent, SizeOf(System.Integer), AHashList, [ghloHashListAsInitData]); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for other number types +{----------------------------------------------------------------------------------------------------------------------} + +class procedure TExtendedHashCode.Currency(constref AValue: Currency; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int64), AHashList, []); +end; + +class procedure TExtendedHashCode.Comp(constref AValue: Comp; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Int64), AHashList, []); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for binary data (records etc) and dynamics arrays +{----------------------------------------------------------------------------------------------------------------------} + +class procedure TExtendedHashCode.Binary(constref AValue; AHashList: PUInt32); +var + _self: PSpoofInterfacedTypeSizeObject absolute Self; +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, _self.Size, AHashList, []); +end; + +class procedure TExtendedHashCode.DynArray(constref AValue: Pointer; AHashList: PUInt32); +var + _self: PSpoofInterfacedTypeSizeObject absolute Self; +begin + EXTENDED_HASH_FACTORY.GetHashList(AValue, DynArraySize(AValue) * _self.Size, AHashList, []); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for classes +{----------------------------------------------------------------------------------------------------------------------} + +class procedure TExtendedHashCode.&Class(constref AValue: TObject; AHashList: PUInt32); +var + LValue: PtrInt; +begin + if AValue = nil then + begin + LValue := $2A; + EXTENDED_HASH_FACTORY.GetHashList(@LValue, SizeOf(LValue), AHashList, []); + Exit; + end; + + LValue := AValue.GetHashCode; + EXTENDED_HASH_FACTORY.GetHashList(@LValue, SizeOf(LValue), AHashList, []); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for string types +{----------------------------------------------------------------------------------------------------------------------} + +class procedure TExtendedHashCode.ShortString1(constref AValue: ShortString1; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue), AHashList, []); +end; + +class procedure TExtendedHashCode.ShortString2(constref AValue: ShortString2; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue), AHashList, []); +end; + +class procedure TExtendedHashCode.ShortString3(constref AValue: ShortString3; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue), AHashList, []); +end; + +class procedure TExtendedHashCode.ShortString(constref AValue: OpenString; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue), AHashList, []); +end; + +class procedure TExtendedHashCode.AnsiString(constref AValue: AnsiString; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue) * SizeOf(System.AnsiChar), AHashList, []); +end; + +class procedure TExtendedHashCode.WideString(constref AValue: WideString; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue) * SizeOf(System.WideChar), AHashList, []); +end; + +class procedure TExtendedHashCode.UnicodeString(constref AValue: UnicodeString; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue[1], Length(AValue) * SizeOf(System.UnicodeChar), AHashList, []); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for Delegates +{----------------------------------------------------------------------------------------------------------------------} + +class procedure TExtendedHashCode.Method(constref AValue: TMethod; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.TMethod), AHashList, []); +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for Variant +{----------------------------------------------------------------------------------------------------------------------} + +class procedure TExtendedHashCode.Variant(constref AValue: PVariant; AHashList: PUInt32); +begin + try + EXTENDED_HASH_FACTORY.UnicodeString(AValue^, AHashList); + except + EXTENDED_HASH_FACTORY.GetHashList(AValue, SizeOf(System.Variant), AHashList, []); + end; +end; + +{----------------------------------------------------------------------------------------------------------------------- + GetHashCode for Pointer +{----------------------------------------------------------------------------------------------------------------------} + +class procedure TExtendedHashCode.Pointer(constref AValue: Pointer; AHashList: PUInt32); +begin + EXTENDED_HASH_FACTORY.GetHashList(@AValue, SizeOf(System.Pointer), AHashList, []); +end; + +{ THashFactory } + +function THashFactory.SelectIntegerEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + case ATypeData.OrdType of + otSByte: + Exit(@FEqualityComparer_Int8_Instance); + otUByte: + Exit(@FEqualityComparer_UInt8_Instance); + otSWord: + Exit(@FEqualityComparer_Int16_Instance); + otUWord: + Exit(@FEqualityComparer_UInt16_Instance); + otSLong: + Exit(@FEqualityComparer_Int32_Instance); + otULong: + Exit(@FEqualityComparer_UInt32_Instance); + else + System.Error(reRangeError); + Exit(nil); + end; +end; + +function THashFactory.SelectFloatEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + case ATypeData.FloatType of + ftSingle: + Exit(@FEqualityComparer_Single_Instance); + ftDouble: + Exit(@FEqualityComparer_Double_Instance); + ftExtended: + Exit(@FEqualityComparer_Extended_Instance); + ftComp: + Exit(@FEqualityComparer_Comp_Instance); + ftCurr: + Exit(@FEqualityComparer_Currency_Instance); + else + System.Error(reRangeError); + Exit(nil); + end; +end; + +function THashFactory.SelectShortStringEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + case ASize of + 2: Exit(@FEqualityComparer_ShortString1_Instance); + 3: Exit(@FEqualityComparer_ShortString2_Instance); + 4: Exit(@FEqualityComparer_ShortString3_Instance); + else + Exit(@FEqualityComparer_ShortString_Instance); + end +end; + +function THashFactory.SelectBinaryEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + case ASize of + 1: Exit(@FEqualityComparer_UInt8_Instance); + 2: Exit(@FEqualityComparer_UInt16_Instance); + 4: Exit(@FEqualityComparer_UInt32_Instance); +{$IFDEF CPU64} + 8: Exit(@FEqualityComparer_UInt64_Instance) +{$ENDIF} + else + Result := CreateInterface(@FEqualityComparer_Binary_VMT, ASize); + end; +end; + +function THashFactory.SelectDynArrayEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + Result := CreateInterface(@FEqualityComparer_DynArray_VMT, ATypeData.elSize); +end; + +function THashFactory.LookupEqualityComparer(ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; +var + LInstance: PInstance; + LSelectMethod: TSelectMethod; +begin + if ATypeInfo = nil then + Exit(SelectBinaryEqualityComparer(GetTypeData(ATypeInfo), ASize)) + else + begin + LInstance := @FEqualityComparerInstances[ATypeInfo.Kind]; + Result := LInstance.Instance; + if LInstance.Selector then + begin + TMethod(LSelectMethod).Code := Result; + TMethod(LSelectMethod).Data := Self; + Result := LSelectMethod(GetTypeData(ATypeInfo), ASize); + end; + end; +end; + +constructor THashFactory.Create; +begin + inherited; + + FEqualityComparer_Int8_VMT := EqualityComparer_Int8_VMT ; + FEqualityComparer_Int16_VMT := EqualityComparer_Int16_VMT ; + FEqualityComparer_Int32_VMT := EqualityComparer_Int32_VMT ; + FEqualityComparer_Int64_VMT := EqualityComparer_Int64_VMT ; + FEqualityComparer_UInt8_VMT := EqualityComparer_UInt8_VMT ; + FEqualityComparer_UInt16_VMT := EqualityComparer_UInt16_VMT ; + FEqualityComparer_UInt32_VMT := EqualityComparer_UInt32_VMT ; + FEqualityComparer_UInt64_VMT := EqualityComparer_UInt64_VMT ; + FEqualityComparer_Single_VMT := EqualityComparer_Single_VMT ; + FEqualityComparer_Double_VMT := EqualityComparer_Double_VMT ; + FEqualityComparer_Extended_VMT := EqualityComparer_Extended_VMT ; + FEqualityComparer_Currency_VMT := EqualityComparer_Currency_VMT ; + FEqualityComparer_Comp_VMT := EqualityComparer_Comp_VMT ; + FEqualityComparer_Binary_VMT := EqualityComparer_Binary_VMT ; + FEqualityComparer_DynArray_VMT := EqualityComparer_DynArray_VMT ; + FEqualityComparer_Class_VMT := EqualityComparer_Class_VMT ; + FEqualityComparer_ShortString1_VMT := EqualityComparer_ShortString1_VMT ; + FEqualityComparer_ShortString2_VMT := EqualityComparer_ShortString2_VMT ; + FEqualityComparer_ShortString3_VMT := EqualityComparer_ShortString3_VMT ; + FEqualityComparer_ShortString_VMT := EqualityComparer_ShortString_VMT ; + FEqualityComparer_AnsiString_VMT := EqualityComparer_AnsiString_VMT ; + FEqualityComparer_WideString_VMT := EqualityComparer_WideString_VMT ; + FEqualityComparer_UnicodeString_VMT := EqualityComparer_UnicodeString_VMT; + FEqualityComparer_Method_VMT := EqualityComparer_Method_VMT ; + FEqualityComparer_Variant_VMT := EqualityComparer_Variant_VMT ; + FEqualityComparer_Pointer_VMT := EqualityComparer_Pointer_VMT ; + + ///// + FEqualityComparer_Int8_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Int16_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Int32_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Int64_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_UInt8_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_UInt16_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_UInt32_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_UInt64_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Single_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Double_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Extended_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Currency_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Comp_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Binary_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_DynArray_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Class_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_ShortString1_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_ShortString2_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_ShortString3_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_ShortString_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_AnsiString_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_WideString_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_UnicodeString_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Method_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Variant_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + FEqualityComparer_Pointer_VMT.__ClassRef := THashFactoryClass(Self.ClassType); + + /////// + FEqualityComparer_Int8_Instance := @FEqualityComparer_Int8_VMT ; + FEqualityComparer_Int16_Instance := @FEqualityComparer_Int16_VMT ; + FEqualityComparer_Int32_Instance := @FEqualityComparer_Int32_VMT ; + FEqualityComparer_Int64_Instance := @FEqualityComparer_Int64_VMT ; + FEqualityComparer_UInt8_Instance := @FEqualityComparer_UInt8_VMT ; + FEqualityComparer_UInt16_Instance := @FEqualityComparer_UInt16_VMT ; + FEqualityComparer_UInt32_Instance := @FEqualityComparer_UInt32_VMT ; + FEqualityComparer_UInt64_Instance := @FEqualityComparer_UInt64_VMT ; + FEqualityComparer_Single_Instance := @FEqualityComparer_Single_VMT ; + FEqualityComparer_Double_Instance := @FEqualityComparer_Double_VMT ; + FEqualityComparer_Extended_Instance := @FEqualityComparer_Extended_VMT ; + FEqualityComparer_Currency_Instance := @FEqualityComparer_Currency_VMT ; + FEqualityComparer_Comp_Instance := @FEqualityComparer_Comp_VMT ; + //FEqualityComparer_Binary_Instance := @FEqualityComparer_Binary_VMT ; // dynamic instance + //FEqualityComparer_DynArray_Instance := @FEqualityComparer_DynArray_VMT ; // dynamic instance + FEqualityComparer_ShortString1_Instance := @FEqualityComparer_ShortString1_VMT ; + FEqualityComparer_ShortString2_Instance := @FEqualityComparer_ShortString2_VMT ; + FEqualityComparer_ShortString3_Instance := @FEqualityComparer_ShortString3_VMT ; + FEqualityComparer_ShortString_Instance := @FEqualityComparer_ShortString_VMT ; + FEqualityComparer_AnsiString_Instance := @FEqualityComparer_AnsiString_VMT ; + FEqualityComparer_WideString_Instance := @FEqualityComparer_WideString_VMT ; + FEqualityComparer_UnicodeString_Instance := @FEqualityComparer_UnicodeString_VMT; + FEqualityComparer_Method_Instance := @FEqualityComparer_Method_VMT ; + FEqualityComparer_Variant_Instance := @FEqualityComparer_Variant_VMT ; + FEqualityComparer_Pointer_Instance := @FEqualityComparer_Pointer_VMT ; + + ////// + FEqualityComparerInstances[tkUnknown] := TInstance.Create(True, @THashFactory.SelectBinaryEqualityComparer); + FEqualityComparerInstances[tkInteger] := TInstance.Create(True, @THashFactory.SelectIntegerEqualityComparer); + FEqualityComparerInstances[tkChar] := TInstance.Create(False, @FEqualityComparer_UInt8_Instance); + FEqualityComparerInstances[tkEnumeration] := TInstance.Create(True, @THashFactory.SelectIntegerEqualityComparer); + FEqualityComparerInstances[tkFloat] := TInstance.Create(True, @THashFactory.SelectFloatEqualityComparer); + FEqualityComparerInstances[tkSet] := TInstance.Create(True, @THashFactory.SelectBinaryEqualityComparer); + FEqualityComparerInstances[tkMethod] := TInstance.Create(False, @FEqualityComparer_Method_Instance); + FEqualityComparerInstances[tkSString] := TInstance.Create(True, @THashFactory.SelectShortStringEqualityComparer); + FEqualityComparerInstances[tkLString] := TInstance.Create(False, @FEqualityComparer_AnsiString_Instance); + FEqualityComparerInstances[tkAString] := TInstance.Create(False, @FEqualityComparer_AnsiString_Instance); + FEqualityComparerInstances[tkWString] := TInstance.Create(False, @FEqualityComparer_WideString_Instance); + FEqualityComparerInstances[tkVariant] := TInstance.Create(False, @FEqualityComparer_Variant_Instance); + FEqualityComparerInstances[tkArray] := TInstance.Create(True, @THashFactory.SelectBinaryEqualityComparer); + FEqualityComparerInstances[tkRecord] := TInstance.Create(True, @THashFactory.SelectBinaryEqualityComparer); + FEqualityComparerInstances[tkInterface] := TInstance.Create(False, @FEqualityComparer_Pointer_Instance); + FEqualityComparerInstances[tkClass] := TInstance.Create(False, @FEqualityComparer_Pointer_Instance); + FEqualityComparerInstances[tkObject] := TInstance.Create(True, @THashFactory.SelectBinaryEqualityComparer); + FEqualityComparerInstances[tkWChar] := TInstance.Create(False, @FEqualityComparer_UInt16_Instance); + FEqualityComparerInstances[tkBool] := TInstance.Create(True, @THashFactory.SelectIntegerEqualityComparer); + FEqualityComparerInstances[tkInt64] := TInstance.Create(False, @FEqualityComparer_Int64_Instance); + FEqualityComparerInstances[tkQWord] := TInstance.Create(False, @FEqualityComparer_UInt64_Instance); + FEqualityComparerInstances[tkDynArray] := TInstance.Create(True, @THashFactory.SelectDynArrayEqualityComparer); + FEqualityComparerInstances[tkInterfaceRaw] := TInstance.Create(False, @FEqualityComparer_Pointer_Instance); + FEqualityComparerInstances[tkProcVar] := TInstance.Create(False, @FEqualityComparer_Pointer_Instance); + FEqualityComparerInstances[tkUString] := TInstance.Create(False, @FEqualityComparer_UnicodeString_Instance); + FEqualityComparerInstances[tkUChar] := TInstance.Create(False, @FEqualityComparer_UInt16_Instance); + FEqualityComparerInstances[tkHelper] := TInstance.Create(False, @FEqualityComparer_Pointer_Instance); + FEqualityComparerInstances[tkFile] := TInstance.Create(True, @THashFactory.SelectBinaryEqualityComparer); + FEqualityComparerInstances[tkClassRef] := TInstance.Create(False, @FEqualityComparer_Pointer_Instance); + FEqualityComparerInstances[tkPointer] := TInstance.Create(False, @FEqualityComparer_Pointer_Instance); +end; + +{ TExtendedHashFactory } + +function TExtendedHashFactory.SelectIntegerEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + case ATypeData.OrdType of + otSByte: + Exit(@FExtendedEqualityComparer_Int8_Instance); + otUByte: + Exit(@FExtendedEqualityComparer_UInt8_Instance); + otSWord: + Exit(@FExtendedEqualityComparer_Int16_Instance); + otUWord: + Exit(@FExtendedEqualityComparer_UInt16_Instance); + otSLong: + Exit(@FExtendedEqualityComparer_Int32_Instance); + otULong: + Exit(@FExtendedEqualityComparer_UInt32_Instance); + else + System.Error(reRangeError); + Exit(nil); + end; +end; + +function TExtendedHashFactory.SelectFloatEqualityComparer(ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + case ATypeData.FloatType of + ftSingle: + Exit(@FExtendedEqualityComparer_Single_Instance); + ftDouble: + Exit(@FExtendedEqualityComparer_Double_Instance); + ftExtended: + Exit(@FExtendedEqualityComparer_Extended_Instance); + ftComp: + Exit(@FExtendedEqualityComparer_Comp_Instance); + ftCurr: + Exit(@FExtendedEqualityComparer_Currency_Instance); + else + System.Error(reRangeError); + Exit(nil); + end; +end; + +function TExtendedHashFactory.SelectShortStringEqualityComparer(ATypeData: PTypeData; + ASize: SizeInt): Pointer; +begin + case ASize of + 2: Exit(@FExtendedEqualityComparer_ShortString1_Instance); + 3: Exit(@FExtendedEqualityComparer_ShortString2_Instance); + 4: Exit(@FExtendedEqualityComparer_ShortString3_Instance); + else + Exit(@FExtendedEqualityComparer_ShortString_Instance); + end +end; + +function TExtendedHashFactory.SelectBinaryEqualityComparer(ATypeData: PTypeData; + ASize: SizeInt): Pointer; +begin + case ASize of + 1: Exit(@FExtendedEqualityComparer_UInt8_Instance); + 2: Exit(@FExtendedEqualityComparer_UInt16_Instance); + 4: Exit(@FExtendedEqualityComparer_UInt32_Instance); +{$IFDEF CPU64} + 8: Exit(@FExtendedEqualityComparer_UInt64_Instance) +{$ENDIF} + else + Result := CreateInterface(@FExtendedEqualityComparer_Binary_VMT, ASize); + end; +end; + +function TExtendedHashFactory.SelectDynArrayEqualityComparer( + ATypeData: PTypeData; ASize: SizeInt): Pointer; +begin + Result := CreateInterface(@FExtendedEqualityComparer_DynArray_VMT, ATypeData.elSize); +end; + +function TExtendedHashFactory.LookupExtendedEqualityComparer( + ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; +var + LInstance: PInstance; + LSelectMethod: TSelectMethod; +begin + if ATypeInfo = nil then + Exit(SelectBinaryEqualityComparer(GetTypeData(ATypeInfo), ASize)) + else + begin + LInstance := @FExtendedEqualityComparerInstances[ATypeInfo.Kind]; + Result := LInstance.Instance; + if LInstance.Selector then + begin + TMethod(LSelectMethod).Code := Result; + TMethod(LSelectMethod).Data := Self; + Result := LSelectMethod(GetTypeData(ATypeInfo), ASize); + end; + end; +end; + +constructor TExtendedHashFactory.Create; +begin + inherited Create; + + FExtendedEqualityComparer_Int8_VMT := ExtendedEqualityComparer_Int8_VMT ; + FExtendedEqualityComparer_Int16_VMT := ExtendedEqualityComparer_Int16_VMT ; + FExtendedEqualityComparer_Int32_VMT := ExtendedEqualityComparer_Int32_VMT ; + FExtendedEqualityComparer_Int64_VMT := ExtendedEqualityComparer_Int64_VMT ; + FExtendedEqualityComparer_UInt8_VMT := ExtendedEqualityComparer_UInt8_VMT ; + FExtendedEqualityComparer_UInt16_VMT := ExtendedEqualityComparer_UInt16_VMT ; + FExtendedEqualityComparer_UInt32_VMT := ExtendedEqualityComparer_UInt32_VMT ; + FExtendedEqualityComparer_UInt64_VMT := ExtendedEqualityComparer_UInt64_VMT ; + FExtendedEqualityComparer_Single_VMT := ExtendedEqualityComparer_Single_VMT ; + FExtendedEqualityComparer_Double_VMT := ExtendedEqualityComparer_Double_VMT ; + FExtendedEqualityComparer_Extended_VMT := ExtendedEqualityComparer_Extended_VMT ; + FExtendedEqualityComparer_Currency_VMT := ExtendedEqualityComparer_Currency_VMT ; + FExtendedEqualityComparer_Comp_VMT := ExtendedEqualityComparer_Comp_VMT ; + FExtendedEqualityComparer_Binary_VMT := ExtendedEqualityComparer_Binary_VMT ; + FExtendedEqualityComparer_DynArray_VMT := ExtendedEqualityComparer_DynArray_VMT ; + FExtendedEqualityComparer_Class_VMT := ExtendedEqualityComparer_Class_VMT ; + FExtendedEqualityComparer_ShortString1_VMT := ExtendedEqualityComparer_ShortString1_VMT ; + FExtendedEqualityComparer_ShortString2_VMT := ExtendedEqualityComparer_ShortString2_VMT ; + FExtendedEqualityComparer_ShortString3_VMT := ExtendedEqualityComparer_ShortString3_VMT ; + FExtendedEqualityComparer_ShortString_VMT := ExtendedEqualityComparer_ShortString_VMT ; + FExtendedEqualityComparer_AnsiString_VMT := ExtendedEqualityComparer_AnsiString_VMT ; + FExtendedEqualityComparer_WideString_VMT := ExtendedEqualityComparer_WideString_VMT ; + FExtendedEqualityComparer_UnicodeString_VMT := ExtendedEqualityComparer_UnicodeString_VMT; + FExtendedEqualityComparer_Method_VMT := ExtendedEqualityComparer_Method_VMT ; + FExtendedEqualityComparer_Variant_VMT := ExtendedEqualityComparer_Variant_VMT ; + FExtendedEqualityComparer_Pointer_VMT := ExtendedEqualityComparer_Pointer_VMT ; + + ///// + FExtendedEqualityComparer_Int8_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Int16_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Int32_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Int64_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_UInt8_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_UInt16_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_UInt32_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_UInt64_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Single_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Double_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Extended_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Currency_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Comp_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Binary_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_DynArray_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Class_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_ShortString1_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_ShortString2_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_ShortString3_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_ShortString_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_AnsiString_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_WideString_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_UnicodeString_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Method_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Variant_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + FExtendedEqualityComparer_Pointer_VMT.__ClassRef := TExtendedHashFactoryClass(Self.ClassType); + + /////// + FExtendedEqualityComparer_Int8_Instance := @FExtendedEqualityComparer_Int8_VMT ; + FExtendedEqualityComparer_Int16_Instance := @FExtendedEqualityComparer_Int16_VMT ; + FExtendedEqualityComparer_Int32_Instance := @FExtendedEqualityComparer_Int32_VMT ; + FExtendedEqualityComparer_Int64_Instance := @FExtendedEqualityComparer_Int64_VMT ; + FExtendedEqualityComparer_UInt8_Instance := @FExtendedEqualityComparer_UInt8_VMT ; + FExtendedEqualityComparer_UInt16_Instance := @FExtendedEqualityComparer_UInt16_VMT ; + FExtendedEqualityComparer_UInt32_Instance := @FExtendedEqualityComparer_UInt32_VMT ; + FExtendedEqualityComparer_UInt64_Instance := @FExtendedEqualityComparer_UInt64_VMT ; + FExtendedEqualityComparer_Single_Instance := @FExtendedEqualityComparer_Single_VMT ; + FExtendedEqualityComparer_Double_Instance := @FExtendedEqualityComparer_Double_VMT ; + FExtendedEqualityComparer_Extended_Instance := @FExtendedEqualityComparer_Extended_VMT ; + FExtendedEqualityComparer_Currency_Instance := @FExtendedEqualityComparer_Currency_VMT ; + FExtendedEqualityComparer_Comp_Instance := @FExtendedEqualityComparer_Comp_VMT ; + //FExtendedEqualityComparer_Binary_Instance := @FExtendedEqualityComparer_Binary_VMT ; // dynamic instance + //FExtendedEqualityComparer_DynArray_Instance := @FExtendedEqualityComparer_DynArray_VMT ; // dynamic instance + FExtendedEqualityComparer_ShortString1_Instance := @FExtendedEqualityComparer_ShortString1_VMT ; + FExtendedEqualityComparer_ShortString2_Instance := @FExtendedEqualityComparer_ShortString2_VMT ; + FExtendedEqualityComparer_ShortString3_Instance := @FExtendedEqualityComparer_ShortString3_VMT ; + FExtendedEqualityComparer_ShortString_Instance := @FExtendedEqualityComparer_ShortString_VMT ; + FExtendedEqualityComparer_AnsiString_Instance := @FExtendedEqualityComparer_AnsiString_VMT ; + FExtendedEqualityComparer_WideString_Instance := @FExtendedEqualityComparer_WideString_VMT ; + FExtendedEqualityComparer_UnicodeString_Instance := @FExtendedEqualityComparer_UnicodeString_VMT; + FExtendedEqualityComparer_Method_Instance := @FExtendedEqualityComparer_Method_VMT ; + FExtendedEqualityComparer_Variant_Instance := @FExtendedEqualityComparer_Variant_VMT ; + FExtendedEqualityComparer_Pointer_Instance := @FExtendedEqualityComparer_Pointer_VMT ; + + ////// + FExtendedEqualityComparerInstances[tkUnknown] := TInstance.Create(True, @TExtendedHashFactory.SelectBinaryEqualityComparer); + FExtendedEqualityComparerInstances[tkInteger] := TInstance.Create(True, @TExtendedHashFactory.SelectIntegerEqualityComparer); + FExtendedEqualityComparerInstances[tkChar] := TInstance.Create(False, @FExtendedEqualityComparer_UInt8_Instance); + FExtendedEqualityComparerInstances[tkEnumeration] := TInstance.Create(True, @TExtendedHashFactory.SelectIntegerEqualityComparer); + FExtendedEqualityComparerInstances[tkFloat] := TInstance.Create(True, @TExtendedHashFactory.SelectFloatEqualityComparer); + FExtendedEqualityComparerInstances[tkSet] := TInstance.Create(True, @TExtendedHashFactory.SelectBinaryEqualityComparer); + FExtendedEqualityComparerInstances[tkMethod] := TInstance.Create(False, @FExtendedEqualityComparer_Method_Instance); + FExtendedEqualityComparerInstances[tkSString] := TInstance.Create(True, @TExtendedHashFactory.SelectShortStringEqualityComparer); + FExtendedEqualityComparerInstances[tkLString] := TInstance.Create(False, @FExtendedEqualityComparer_AnsiString_Instance); + FExtendedEqualityComparerInstances[tkAString] := TInstance.Create(False, @FExtendedEqualityComparer_AnsiString_Instance); + FExtendedEqualityComparerInstances[tkWString] := TInstance.Create(False, @FExtendedEqualityComparer_WideString_Instance); + FExtendedEqualityComparerInstances[tkVariant] := TInstance.Create(False, @FExtendedEqualityComparer_Variant_Instance); + FExtendedEqualityComparerInstances[tkArray] := TInstance.Create(True, @TExtendedHashFactory.SelectBinaryEqualityComparer); + FExtendedEqualityComparerInstances[tkRecord] := TInstance.Create(True, @TExtendedHashFactory.SelectBinaryEqualityComparer); + FExtendedEqualityComparerInstances[tkInterface] := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance); + FExtendedEqualityComparerInstances[tkClass] := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance); + FExtendedEqualityComparerInstances[tkObject] := TInstance.Create(True, @TExtendedHashFactory.SelectBinaryEqualityComparer); + FExtendedEqualityComparerInstances[tkWChar] := TInstance.Create(False, @FExtendedEqualityComparer_UInt16_Instance); + FExtendedEqualityComparerInstances[tkBool] := TInstance.Create(True, @TExtendedHashFactory.SelectIntegerEqualityComparer); + FExtendedEqualityComparerInstances[tkInt64] := TInstance.Create(False, @FExtendedEqualityComparer_Int64_Instance); + FExtendedEqualityComparerInstances[tkQWord] := TInstance.Create(False, @FExtendedEqualityComparer_UInt64_Instance); + FExtendedEqualityComparerInstances[tkDynArray] := TInstance.Create(True, @TExtendedHashFactory.SelectDynArrayEqualityComparer); + FExtendedEqualityComparerInstances[tkInterfaceRaw] := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance); + FExtendedEqualityComparerInstances[tkProcVar] := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance); + FExtendedEqualityComparerInstances[tkUString] := TInstance.Create(False, @FExtendedEqualityComparer_UnicodeString_Instance); + FExtendedEqualityComparerInstances[tkUChar] := TInstance.Create(False, @FExtendedEqualityComparer_UInt16_Instance); + FExtendedEqualityComparerInstances[tkHelper] := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance); + FExtendedEqualityComparerInstances[tkFile] := TInstance.Create(True, @TExtendedHashFactory.SelectBinaryEqualityComparer); + FExtendedEqualityComparerInstances[tkClassRef] := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance); + FExtendedEqualityComparerInstances[tkPointer] := TInstance.Create(False, @FExtendedEqualityComparer_Pointer_Instance); +end; + +{ TEqualityComparer<T> } + +class function TEqualityComparer<T>.Default: IEqualityComparer<T>; +begin + Result := _LookupVtableInfo(giEqualityComparer, TypeInfo(T), SizeOf(T)); +end; + +class function TEqualityComparer<T>.Default(AHashFactoryClass: TComparerFactoryClass): IEqualityComparer<T>; +begin + if AHashFactoryClass.InheritsFrom(THashFactory) then + Result := _LookupVtableInfoEx(giEqualityComparer, TypeInfo(T), SizeOf(T), AHashFactoryClass) + else if AHashFactoryClass.InheritsFrom(TExtendedHashFactory) then + Result := _LookupVtableInfoEx(giExtendedEqualityComparer, TypeInfo(T), SizeOf(T), AHashFactoryClass) +end; + +class function TEqualityComparer<T>.Construct(const AEqualityComparison: TOnEqualityComparison<T>; + const AHasher: TOnHasher<T>): IEqualityComparer<T>; +begin + Result := TDelegatedEqualityComparerEvents<T>.Create(AEqualityComparison, AHasher); +end; + +class function TEqualityComparer<T>.Construct(const AEqualityComparison: TEqualityComparisonFunc<T>; + const AHasher: THasherFunc<T>): IEqualityComparer<T>; +begin + Result := TDelegatedEqualityComparerFunc<T>.Create(AEqualityComparison, AHasher); +end; + +{ TDelegatedEqualityComparerEvents<T> } + +function TDelegatedEqualityComparerEvents<T>.Equals(constref ALeft, ARight: T): Boolean; +begin + Result := FEqualityComparison(ALeft, ARight); +end; + +function TDelegatedEqualityComparerEvents<T>.GetHashCode(constref AValue: T): UInt32; +begin + Result := FHasher(AValue); +end; + +constructor TDelegatedEqualityComparerEvents<T>.Create(const AEqualityComparison: TOnEqualityComparison<T>; + const AHasher: TOnHasher<T>); +begin + FEqualityComparison := AEqualityComparison; + FHasher := AHasher; +end; + +{ TDelegatedEqualityComparerFunc<T> } + +function TDelegatedEqualityComparerFunc<T>.Equals(constref ALeft, ARight: T): Boolean; +begin + Result := FEqualityComparison(ALeft, ARight); +end; + +function TDelegatedEqualityComparerFunc<T>.GetHashCode(constref AValue: T): UInt32; +begin + Result := FHasher(AValue); +end; + +constructor TDelegatedEqualityComparerFunc<T>.Create(const AEqualityComparison: TEqualityComparisonFunc<T>; + const AHasher: THasherFunc<T>); +begin + FEqualityComparison := AEqualityComparison; + FHasher := AHasher; +end; + +{ TDelegatedExtendedEqualityComparerEvents<T> } + +function TDelegatedExtendedEqualityComparerEvents<T>.GetHashCodeMethod(constref AValue: T): UInt32; +var + LHashList: array[0..1] of Int32; + LHashListParams: array[0..3] of Int16 absolute LHashList; +begin + LHashListParams[0] := -1; + FExtendedHasher(AValue, @LHashList[0]); + Result := LHashList[1]; +end; + +function TDelegatedExtendedEqualityComparerEvents<T>.Equals(constref ALeft, ARight: T): Boolean; +begin + Result := FEqualityComparison(ALeft, ARight); +end; + +function TDelegatedExtendedEqualityComparerEvents<T>.GetHashCode(constref AValue: T): UInt32; +begin + Result := FHasher(AValue); +end; + +procedure TDelegatedExtendedEqualityComparerEvents<T>.GetHashList(constref AValue: T; AHashList: PUInt32); +begin + FExtendedHasher(AValue, AHashList); +end; + +constructor TDelegatedExtendedEqualityComparerEvents<T>.Create(const AEqualityComparison: TOnEqualityComparison<T>; + const AHasher: TOnHasher<T>; const AExtendedHasher: TOnExtendedHasher<T>); +begin + FEqualityComparison := AEqualityComparison; + FHasher := AHasher; + FExtendedHasher := AExtendedHasher; +end; + +constructor TDelegatedExtendedEqualityComparerEvents<T>.Create(const AEqualityComparison: TOnEqualityComparison<T>; + const AExtendedHasher: TOnExtendedHasher<T>); +begin + Create(AEqualityComparison, GetHashCodeMethod, AExtendedHasher); +end; + +{ TDelegatedExtendedEqualityComparerFunc<T> } + +function TDelegatedExtendedEqualityComparerFunc<T>.Equals(constref ALeft, ARight: T): Boolean; +begin + Result := FEqualityComparison(ALeft, ARight); +end; + +function TDelegatedExtendedEqualityComparerFunc<T>.GetHashCode(constref AValue: T): UInt32; +var + LHashList: array[0..1] of Int32; + LHashListParams: array[0..3] of Int16 absolute LHashList; +begin + if not Assigned(FHasher) then + begin + LHashListParams[0] := -1; + FExtendedHasher(AValue, @LHashList[0]); + Result := LHashList[1]; + end + else + Result := FHasher(AValue); +end; + +procedure TDelegatedExtendedEqualityComparerFunc<T>.GetHashList(constref AValue: T; AHashList: PUInt32); +begin + FExtendedHasher(AValue, AHashList); +end; + +constructor TDelegatedExtendedEqualityComparerFunc<T>.Create(const AEqualityComparison: TEqualityComparisonFunc<T>; + const AHasher: THasherFunc<T>; const AExtendedHasher: TExtendedHasherFunc<T>); +begin + FEqualityComparison := AEqualityComparison; + FHasher := AHasher; + FExtendedHasher := AExtendedHasher; +end; + +constructor TDelegatedExtendedEqualityComparerFunc<T>.Create(const AEqualityComparison: TEqualityComparisonFunc<T>; + const AExtendedHasher: TExtendedHasherFunc<T>); +begin + Create(AEqualityComparison, nil, AExtendedHasher); +end; + +{ TExtendedEqualityComparer<T> } + +class function TExtendedEqualityComparer<T>.Default: IExtendedEqualityComparer<T>; +begin + Result := _LookupVtableInfo(giExtendedEqualityComparer, TypeInfo(T), SizeOf(T)); +end; + +class function TExtendedEqualityComparer<T>.Default( + AExtenedHashFactoryClass: TExtendedHashFactoryClass + ): IExtendedEqualityComparer; +begin + Result := _LookupVtableInfoEx(giExtendedEqualityComparer, TypeInfo(T), SizeOf(T), AExtenedHashFactoryClass); +end; + +class function TExtendedEqualityComparer<T>.Construct( + const AEqualityComparison: TOnEqualityComparison<T>; const AHasher: TOnHasher<T>; + const AExtendedHasher: TOnExtendedHasher<T>): IExtendedEqualityComparer<T>; +begin + Result := TDelegatedExtendedEqualityComparerEvents<T>.Create(AEqualityComparison, AHasher, AExtendedHasher); +end; + +class function TExtendedEqualityComparer<T>.Construct( + const AEqualityComparison: TEqualityComparisonFunc<T>; const AHasher: THasherFunc<T>; + const AExtendedHasher: TExtendedHasherFunc<T>): IExtendedEqualityComparer<T>; +begin + Result := TDelegatedExtendedEqualityComparerFunc<T>.Create(AEqualityComparison, AHasher, AExtendedHasher); +end; + +class function TExtendedEqualityComparer<T>.Construct( + const AEqualityComparison: TOnEqualityComparison<T>; + const AExtendedHasher: TOnExtendedHasher<T>): IExtendedEqualityComparer<T>; +begin + Result := TDelegatedExtendedEqualityComparerEvents<T>.Create(AEqualityComparison, AExtendedHasher); +end; + +class function TExtendedEqualityComparer<T>.Construct( + const AEqualityComparison: TEqualityComparisonFunc<T>; + const AExtendedHasher: TExtendedHasherFunc<T>): IExtendedEqualityComparer<T>; +begin + Result := TDelegatedExtendedEqualityComparerFunc<T>.Create(AEqualityComparison, AExtendedHasher); +end; + +{ TDelphiHashFactory } + +class constructor TDelphiHashFactory.Create; +begin + FID := Register(TDelphiHashFactory); +end; + +class function TDelphiHashFactory.GetID: Integer; +begin + Result := FID; +end; + +class function TDelphiHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32; +begin + Result := DelphiHashLittle(AKey, ASize, AInitVal); +end; + +{ TAdler32HashFactory } + +class constructor TAdler32HashFactory.Create; +begin + FID := Register(TAdler32HashFactory); +end; + +class function TAdler32HashFactory.GetID: Integer; +begin + Result := FID; +end; + +class function TAdler32HashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; + AInitVal: UInt32): UInt32; +begin + Result := Adler32(AKey, ASize); +end; + +{ TSdbmHashFactory } + +class constructor TSdbmHashFactory.Create; +begin + FID := Register(TSdbmHashFactory); +end; + +class function TSdbmHashFactory.GetID: Integer; +begin + Result := FID; +end; + +class function TSdbmHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; + AInitVal: UInt32): UInt32; +begin + Result := sdbm(AKey, ASize); +end; + +{ TSimpleChecksumFactory } + +class constructor TSimpleChecksumFactory.Create; +begin + FID := Register(TSimpleChecksumFactory); +end; + +class function TSimpleChecksumFactory.GetID: Integer; +begin + Result := FID; +end; + +class function TSimpleChecksumFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; + AInitVal: UInt32): UInt32; +begin + Result := SimpleChecksumHash(AKey, ASize); +end; + +{ TDelphiDoubleHashFactory } + +class constructor TDelphiDoubleHashFactory.Create; +begin + FID := Register(TDelphiDoubleHashFactory); +end; + +class function TDelphiDoubleHashFactory.GetID: Integer; +begin + Result := FID; +end; + +class function TDelphiDoubleHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32; +begin + Result := DelphiHashLittle(AKey, ASize, AInitVal); +end; + +class procedure TDelphiDoubleHashFactory.GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; + AOptions: TGetHashListOptions); +var + LHash: UInt32; + AHashListParams: PUInt16 absolute AHashList; +begin +{$WARNINGS OFF} + case AHashListParams[0] of + -2: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 0; + LHash := 0; + DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]); + Exit; + end; + -1: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 0; + LHash := 0; + DelphiHashLittle2(AKey, ASize, AHashList[1], LHash); + Exit; + end; + 0: Exit; + 1: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 0; + LHash := 0; + DelphiHashLittle2(AKey, ASize, AHashList[1], LHash); + Exit; + end; + 2: + begin + if not (ghloHashListAsInitData in AOptions) then + begin + AHashList[1] := 0; + AHashList[2] := 0; + end; + DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]); + Exit; + end; + else + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + end; +{$WARNINGS ON} +end; + +{ TDelphiQuadrupleHashFactory } + +class constructor TDelphiQuadrupleHashFactory.Create; +begin + FID := Register(TDelphiQuadrupleHashFactory); +end; + +class function TDelphiQuadrupleHashFactory.GetID: Integer; +begin + Result := FID; +end; + +class function TDelphiQuadrupleHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32; +begin + Result := DelphiHashLittle(AKey, ASize, AInitVal); +end; + +class procedure TDelphiQuadrupleHashFactory.GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; + AOptions: TGetHashListOptions); +var + LHash: UInt32; + AHashListParams: PInt16 absolute AHashList; +begin + case AHashListParams[0] of + -4: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 1988; + LHash := 2004; + DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]); + Exit; + end; + -3: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 2004; + LHash := 1988; + DelphiHashLittle2(AKey, ASize, AHashList[1], LHash); + Exit; + end; + -2: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 0; + LHash := 0; + DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]); + Exit; + end; + -1: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 0; + LHash := 0; + DelphiHashLittle2(AKey, ASize, AHashList[1], LHash); + Exit; + end; + 0: Exit; + 1: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 0; + LHash := 0; + DelphiHashLittle2(AKey, ASize, AHashList[1], LHash); + Exit; + end; + 2: + begin + case AHashListParams[1] of + 0, 1: + begin + if not (ghloHashListAsInitData in AOptions) then + begin + AHashList[1] := 0; + AHashList[2] := 0; + end; + DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]); + Exit; + end; + 2: + begin + if not (ghloHashListAsInitData in AOptions) then + begin + AHashList[1] := 2004; + AHashList[2] := 1988; + end; + DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]); + Exit; + end; + else + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + end; + end; + 4: + case AHashListParams[1] of + 1: + begin + if not (ghloHashListAsInitData in AOptions) then + begin + AHashList[1] := 0; + AHashList[2] := 0; + end; + DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]); + Exit; + end; + 2: + begin + if not (ghloHashListAsInitData in AOptions) then + begin + AHashList[3] := 2004; + AHashList[4] := 1988; + end; + DelphiHashLittle2(AKey, ASize, AHashList[3], AHashList[4]); + Exit; + end; + else + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + end; + else + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + end; +end; + +{ TDelphiSixfoldHashFactory } + +class constructor TDelphiSixfoldHashFactory.Create; +begin + FID := Register(TDelphiSixfoldHashFactory); +end; + +class function TDelphiSixfoldHashFactory.GetID: Integer; +begin + Result := FID; +end; + +class function TDelphiSixfoldHashFactory.GetHashCode(AKey: Pointer; ASize: SizeInt; AInitVal: UInt32): UInt32; +begin + Result := DelphiHashLittle(AKey, ASize, AInitVal); +end; + +class procedure TDelphiSixfoldHashFactory.GetHashList(AKey: Pointer; ASize: SizeInt; AHashList: PUInt32; + AOptions: TGetHashListOptions); +var + LHash: UInt32; + AHashListParams: PInt16 absolute AHashList; +begin + case AHashListParams[0] of + -6: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 2; + LHash := 1; + DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]); + Exit; + end; + -5: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 1; + LHash := 2; + DelphiHashLittle2(AKey, ASize, AHashList[1], LHash); + Exit; + end; + -4: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 1988; + LHash := 2004; + DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]); + Exit; + end; + -3: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 2004; + LHash := 1988; + DelphiHashLittle2(AKey, ASize, AHashList[1], LHash); + Exit; + end; + -2: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 0; + LHash := 0; + DelphiHashLittle2(AKey, ASize, LHash, AHashList[1]); + Exit; + end; + -1: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 0; + LHash := 0; + DelphiHashLittle2(AKey, ASize, AHashList[1], LHash); + Exit; + end; + 0: Exit; + 1: + begin + if not (ghloHashListAsInitData in AOptions) then + AHashList[1] := 0; + LHash := 0; + DelphiHashLittle2(AKey, ASize, AHashList[1], LHash); + Exit; + end; + 2: + begin + case AHashListParams[1] of + 0, 1: + begin + if not (ghloHashListAsInitData in AOptions) then + begin + AHashList[1] := 0; + AHashList[2] := 0; + end; + DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]); + Exit; + end; + 2: + begin + if not (ghloHashListAsInitData in AOptions) then + begin + AHashList[1] := 2004; + AHashList[2] := 1988; + end; + DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]); + Exit; + end; + else + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + end; + end; + 6: + case AHashListParams[1] of + 1: + begin + if not (ghloHashListAsInitData in AOptions) then + begin + AHashList[1] := 0; + AHashList[2] := 0; + end; + DelphiHashLittle2(AKey, ASize, AHashList[1], AHashList[2]); + Exit; + end; + 2: + begin + if not (ghloHashListAsInitData in AOptions) then + begin + AHashList[3] := 2004; + AHashList[4] := 1988; + end; + DelphiHashLittle2(AKey, ASize, AHashList[3], AHashList[4]); + Exit; + end; + 3: + begin + if not (ghloHashListAsInitData in AOptions) then + begin + AHashList[5] := 1; + AHashList[6] := 2; + end; + DelphiHashLittle2(AKey, ASize, AHashList[5], AHashList[6]); + Exit; + end; + else + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + end; + else + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + end; +end; + +{ TOrdinalComparer<T, THashFactory> } + +class constructor TOrdinalComparer<T, THashFactory>.Create; +begin + if THashFactory.InheritsFrom(TExtendedHashFactory) then + begin + FExtendedEqualityComparer := TExtendedEqualityComparer<T>.Default(TExtendedHashFactoryClass(THashFactory)); + FEqualityComparer := IEqualityComparer<T>(FExtendedEqualityComparer); + end + else + FEqualityComparer := TEqualityComparer<T>.Default(THashFactory); + FComparer := TComparer<T>.Default; +end; + +{ TGStringComparer<T, THashFactory> } + +class destructor TGStringComparer<T, THashFactory>.Destroy; +begin + if Assigned(FOrdinal) then + FOrdinal.Free; +end; + +class function TGStringComparer<T, THashFactory>.Ordinal: TCustomComparer<T>; +begin + if not Assigned(FOrdinal) then + FOrdinal := TGOrdinalStringComparer<T, THashFactory>.Create; + Result := FOrdinal; +end; + +{ TGOrdinalStringComparer<T, THashFactory> } + +function TGOrdinalStringComparer<T, THashFactory>.Compare(constref ALeft, ARight: T): Integer; +begin + Result := FComparer.Compare(ALeft, ARight); +end; + +function TGOrdinalStringComparer<T, THashFactory>.Equals(constref ALeft, ARight: T): Boolean; +begin + Result := FEqualityComparer.Equals(ALeft, ARight); +end; + +function TGOrdinalStringComparer<T, THashFactory>.GetHashCode(constref AValue: T): UInt32; +begin + Result := FEqualityComparer.GetHashCode(AValue); +end; + +procedure TGOrdinalStringComparer<T, THashFactory>.GetHashList(constref AValue: T; AHashList: PUInt32); +begin + FExtendedEqualityComparer.GetHashList(AValue, AHashList); +end; + +{ TGIStringComparer<T, THashFactory> } + +class destructor TGIStringComparer<T, THashFactory>.Destroy; +begin + if Assigned(FOrdinal) then + FOrdinal.Free; +end; + +class function TGIStringComparer<T, THashFactory>.Ordinal: TCustomComparer<T>; +begin + if not Assigned(FOrdinal) then + FOrdinal := TGOrdinalIStringComparer<T, THashFactory>.Create; + Result := FOrdinal; +end; + +{ TGOrdinalIStringComparer<T, THashFactory> } + +function TGOrdinalIStringComparer<T, THashFactory>.Compare(constref ALeft, ARight: T): Integer; +begin + Result := FComparer.Compare(ALeft.ToLower, ARight.ToLower); +end; + +function TGOrdinalIStringComparer<T, THashFactory>.Equals(constref ALeft, ARight: T): Boolean; +begin + Result := FEqualityComparer.Equals(ALeft.ToLower, ARight.ToLower); +end; + +function TGOrdinalIStringComparer<T, THashFactory>.GetHashCode(constref AValue: T): UInt32; +begin + Result := FEqualityComparer.GetHashCode(AValue.ToLower); +end; + +procedure TGOrdinalIStringComparer<T, THashFactory>.GetHashList(constref AValue: T; AHashList: PUInt32); +begin + FExtendedEqualityComparer.GetHashList(AValue.ToLower, AHashList); +end; + +function BobJenkinsHash(const AData; ALength, AInitData: Integer): Integer; +begin + Result := DelphiHashLittle(@AData, ALength, AInitData); +end; + +function BinaryCompare(const ALeft, ARight: Pointer; ASize: PtrUInt): Integer; +begin + Result := CompareMemRange(ALeft, ARight, ASize); +end; + +function _LookupVtableInfo(AGInterface: TDefaultGenericInterface; ATypeInfo: PTypeInfo; ASize: SizeInt): Pointer; +begin + Result := _LookupVtableInfoEx(AGInterface, ATypeInfo, ASize, nil); +end; + +function _LookupVtableInfoEx(AGInterface: TDefaultGenericInterface; ATypeInfo: PTypeInfo; ASize: SizeInt; + AFactory: TComparerFactoryClass): Pointer; +begin + case AGInterface of + giComparer: + Exit( + THashFactory.LookupComparer(ATypeInfo, ASize)); + giEqualityComparer: + begin + if AFactory = nil then + AFactory := TDelphiHashFactory; + + Exit( + ComparerFactory[AFactory.GetID].LookupEqualityComparer(ATypeInfo, ASize)); + end; + giExtendedEqualityComparer: + begin + if AFactory = nil then + AFactory := TDelphiDoubleHashFactory; + + Exit( + ComparerFactory[AFactory.GetID].LookupExtendedEqualityComparer(ATypeInfo, ASize)); + end; + else + System.Error(reRangeError); + Exit(nil); + end; +end; + +procedure FreeComparerFactory; +var + i: Integer; +begin + for i := 0 to High(ComparerFactory) do + ComparerFactory[i].Free; + + SetLength(ComparerFactory, 0); + ComparerFactory := nil; +end; + +finalization + FreeComparerFactory; +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.hashes.pas b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.hashes.pas new file mode 100644 index 000000000..73a9b3c91 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.hashes.pas @@ -0,0 +1,913 @@ +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2014 by Maciej Izak (hnb) + member of the Free Sparta development team (http://freesparta.com) + + Copyright(c) 2004-2014 DaThoX + + It contains the Free Pascal generics library + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} + +unit Generics.Hashes; + +{$MODE DELPHI}{$H+} +{$POINTERMATH ON} +{$MACRO ON} +{$COPERATORS ON} + +interface + +uses + Classes, SysUtils; + +// Original version of Bob Jenkins Hash +// http://burtleburtle.net/bob/c/lookup3.c +function HashWord( + AKey: PLongWord; //* the key, an array of uint32_t values */ + ALength: SizeInt; //* the length of the key, in uint32_ts */ + AInitVal: UInt32): UInt32; //* the previous hash, or an arbitrary value */ +procedure HashWord2 ( + AKey: PLongWord; //* the key, an array of uint32_t values */ + ALength: SizeInt; //* the length of the key, in uint32_ts */ + var APrimaryHashAndInitVal: UInt32; //* IN: seed OUT: primary hash value */ + var ASecondaryHashAndInitVal: UInt32); //* IN: more seed OUT: secondary hash value */ + +function HashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): UInt32; +procedure HashLittle2( + AKey: Pointer; //* the key to hash */ + ALength: SizeInt; //* length of the key */ + var APrimaryHashAndInitVal: UInt32; //* IN: primary initval, OUT: primary hash */ + var ASecondaryHashAndInitVal: UInt32); //* IN: secondary initval, OUT: secondary hash */ + +function DelphiHashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): Int32; +procedure DelphiHashLittle2(AKey: Pointer; ALength: SizeInt; var APrimaryHashAndInitVal, ASecondaryHashAndInitVal: UInt32); + +// hash function from fstl +function SimpleChecksumHash(AKey: Pointer; ALength: SizeInt): UInt32; + +// some other hashes +// http://stackoverflow.com/questions/14409466/simple-hash-functions +// http://www.partow.net/programming/hashfunctions/ +// http://en.wikipedia.org/wiki/List_of_hash_functions +// http://www.cse.yorku.ca/~oz/hash.html + +// https://code.google.com/p/hedgewars/source/browse/hedgewars/adler32.pas +function Adler32(AKey: Pointer; ALength: SizeInt): UInt32; +function sdbm(AKey: Pointer; ALength: SizeInt): UInt32; + +implementation + +function SimpleChecksumHash(AKey: Pointer; ALength: SizeInt): UInt32; +var + i: Integer; + ABuffer: PUInt8 absolute AKey; +begin + Result := 0; + for i := 0 to ALength - 1 do + Inc(Result,ABuffer[i]); +end; + +function Adler32(AKey: Pointer; ALength: SizeInt): UInt32; +const + MOD_ADLER = 65521; +var + ABuffer: PUInt8 absolute AKey; + a: UInt32 = 1; + b: UInt32 = 0; + n: Integer; +begin + for n := 0 to ALength -1 do + begin + a := (a + ABuffer[n]) mod MOD_ADLER; + b := (b + a) mod MOD_ADLER; + end; + Result := (b shl 16) or a; +end; + +function sdbm(AKey: Pointer; ALength: SizeInt): UInt32; +var + c: PUInt8 absolute AKey; + i: Integer; +begin + Result := 0; + c := AKey; + for i := 0 to ALength - 1 do + begin + Result := c^ + (Result shl 6) + (Result shl 16) {%H-}- Result; + Inc(c); + end; +end; + +{ BobJenkinsHash } + +{$define mix_abc := + a -= c; a := a xor (((c)shl(4)) or ((c)shr(32-(4)))); c += b; + b -= a; b := b xor (((a)shl(6)) or ((a)shr(32-(6)))); a += c; + c -= b; c := c xor (((b)shl(8)) or ((b)shr(32-(8)))); b += a; + a -= c; a := a xor (((c)shl(16)) or ((c)shr(32-(16)))); c += b; + b -= a; b := b xor (((a)shl(19)) or ((a)shr(32-(19)))); a += c; + c -= b; c := c xor (((b)shl(4)) or ((b)shr(32-(4)))); b += a +} + +{$define final_abc := + c := c xor b; c -= (((b)shl(14)) or ((b)shr(32-(14)))); + a := a xor c; a -= (((c)shl(11)) or ((c)shr(32-(11)))); + b := b xor a; b -= (((a)shl(25)) or ((a)shr(32-(25)))); + c := c xor b; c -= (((b)shl(16)) or ((b)shr(32-(16)))); + a := a xor c; a -= (((c)shl(4)) or ((c)shr(32-(4)))); + b := b xor a; b -= (((a)shl(14)) or ((a)shr(32-(14)))); + c := c xor b; c -= (((b)shl(24)) or ((b)shr(32-(24)))) +} + +function HashWord( + AKey: PLongWord; //* the key, an array of uint32_t values */ + ALength: SizeInt; //* the length of the key, in uint32_ts */ + AInitVal: UInt32): UInt32; //* the previous hash, or an arbitrary value */ +var + a,b,c: UInt32; +label + Case0, Case1, Case2, Case3; +begin + //* Set up the internal state */ + a := $DEADBEEF + (UInt32(ALength) shl 2) + AInitVal; + b := a; + c := b; + + //*------------------------------------------------- handle most of the key */ + while ALength > 3 do + begin + a += AKey[0]; + b += AKey[1]; + c += AKey[2]; + mix_abc; + ALength -= 3; + AKey += 3; + end; + + //*------------------------------------------- handle the last 3 uint32_t's */ + case ALength of //* all the case statements fall through */ + 3: goto Case3; + 2: goto Case2; + 1: goto Case1; + 0: goto Case0; + end; + Case3: c+=AKey[2]; + Case2: b+=AKey[1]; + Case1: a+=AKey[0]; + final_abc; + Case0: //* case 0: nothing left to add */ + //*------------------------------------------------------ report the result */ + Result := c; +end; + +procedure HashWord2 ( +AKey: PLongWord; //* the key, an array of uint32_t values */ +ALength: SizeInt; //* the length of the key, in uint32_ts */ +var APrimaryHashAndInitVal: UInt32; //* IN: seed OUT: primary hash value */ +var ASecondaryHashAndInitVal: UInt32); //* IN: more seed OUT: secondary hash value */ +var + a,b,c: UInt32; +label + Case0, Case1, Case2, Case3; +begin + //* Set up the internal state */ + a := $deadbeef + (UInt32(ALength shl 2)) + APrimaryHashAndInitVal; + b := a; + c := b; + c += ASecondaryHashAndInitVal; + + //*------------------------------------------------- handle most of the key */ + while ALength > 3 do + begin + a += AKey[0]; + b += AKey[1]; + c += AKey[2]; + mix_abc; + ALength -= 3; + AKey += 3; + end; + + //*------------------------------------------- handle the last 3 uint32_t's */ + case ALength of //* all the case statements fall through */ + 3: goto Case3; + 2: goto Case2; + 1: goto Case1; + 0: goto Case0; + end; + Case3: c+=AKey[2]; + Case2: b+=AKey[1]; + Case1: a+=AKey[0]; + final_abc; + Case0: //* case 0: nothing left to add */ + //*------------------------------------------------------ report the result */ + APrimaryHashAndInitVal := c; + ASecondaryHashAndInitVal := b; +end; + +function HashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): UInt32; +var + a, b, c: UInt32; + u: record case byte of + 0: (ptr: Pointer); + 1: (i: PtrUint); + end absolute AKey; + + k32: ^UInt32 absolute AKey; + k16: ^UInt16 absolute AKey; + k8: ^UInt8 absolute AKey; + +label _10, _8, _6, _4, _2; +label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1; + +begin + a := $DEADBEEF + UInt32(ALength) + AInitVal; + b := a; + c := b; + +{$IFDEF ENDIAN_LITTLE} + if (u.i and $3) = 0 then + begin + while (ALength > 12) do + begin + a += k32[0]; + b += k32[1]; + c += k32[2]; + mix_abc; + ALength -= 12; + k32 += 3; + end; + + case ALength of + 12: begin c += k32[2]; b += k32[1]; a += k32[0]; end; + 11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end; + 10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end; + 9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end; + 8 : begin b += k32[1]; a += k32[0]; end; + 7 : begin b += k32[1] and $ffffff; a += k32[0]; end; + 6 : begin b += k32[1] and $ffff; a += k32[0]; end; + 5 : begin b += k32[1] and $ff; a += k32[0]; end; + 4 : begin a += k32[0]; end; + 3 : begin a += k32[0] and $ffffff; end; + 2 : begin a += k32[0] and $ffff; end; + 1 : begin a += k32[0] and $ff; end; + 0 : Exit(c); // zero length strings require no mixing + end + end + else + if (u.i and $1) = 0 then + begin + while (ALength > 12) do + begin + a += k16[0] + (UInt32(k16[1]) shl 16); + b += k16[2] + (UInt32(k16[3]) shl 16); + c += k16[4] + (UInt32(k16[5]) shl 16); + mix_abc; + ALength -= 12; + k16 += 6; + end; + + case ALength of + 12: + begin + c+=k16[4]+((UInt32(k16[5])) shl 16); + b+=k16[2]+((UInt32(k16[3])) shl 16); + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 11: + begin + c+=(UInt32(k8[10])) shl 16; //* fall through */ + goto _10; + end; + 10: + begin _10: + c+=k16[4]; + b+=k16[2]+((UInt32(k16[3])) shl 16); + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 9 : + begin + c+=k8[8]; //* fall through */ + goto _8; + end; + 8 : + begin _8: + b+=k16[2]+((UInt32(k16[3])) shl 16); + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 7 : + begin + b+=(UInt32(k8[6])) shl 16; //* fall through */ + goto _6; + end; + 6 : + begin _6: + b+=k16[2]; + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 5 : + begin + b+=k8[4]; //* fall through */ + goto _4; + end; + 4 : + begin _4: + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 3 : + begin + a+=(UInt32(k8[2])) shl 16; //* fall through */ + goto _2; + end; + 2 : + begin _2: + a+=k16[0]; + end; + 1 : + begin + a+=k8[0]; + end; + 0 : Exit(c); //* zero length requires no mixing */ + end; + end + else +{$ENDIF} + begin + while ALength > 12 do + begin + a += k8[0]; + a += (UInt32(k8[1])) shl 8; + a += (UInt32(k8[2])) shl 16; + a += (UInt32(k8[3])) shl 24; + b += k8[4]; + b += (UInt32(k8[5])) shl 8; + b += (UInt32(k8[6])) shl 16; + b += (UInt32(k8[7])) shl 24; + c += k8[8]; + c += (UInt32(k8[9])) shl 8; + c += (UInt32(k8[10])) shl 16; + c += (UInt32(k8[11])) shl 24; + mix_abc; + ALength -= 12; + k8 += 12; + end; + + case ALength of + 12: goto Case12; + 11: goto Case11; + 10: goto Case10; + 9 : goto Case9; + 8 : goto Case8; + 7 : goto Case7; + 6 : goto Case6; + 5 : goto Case5; + 4 : goto Case4; + 3 : goto Case3; + 2 : goto Case2; + 1 : goto Case1; + 0 : Exit(c); + end; + + Case12: c+=(UInt32(k8[11])) shl 24; + Case11: c+=(UInt32(k8[10])) shl 16; + Case10: c+=(UInt32(k8[9])) shl 8; + Case9: c+=k8[8]; + Case8: b+=(UInt32(k8[7])) shl 24; + Case7: b+=(UInt32(k8[6])) shl 16; + Case6: b+=(UInt32(k8[5])) shl 8; + Case5: b+=k8[4]; + Case4: a+=(UInt32(k8[3])) shl 24; + Case3: a+=(UInt32(k8[2])) shl 16; + Case2: a+=(UInt32(k8[1])) shl 8; + Case1: a+=k8[0]; + end; + + final_abc; + Result := c; +end; + +(* + * hashlittle2: return 2 32-bit hash values + * + * This is identical to hashlittle(), except it returns two 32-bit hash + * values instead of just one. This is good enough for hash table + * lookup with 2^^64 buckets, or if you want a second hash if you're not + * happy with the first, or if you want a probably-unique 64-bit ID for + * the key. *pc is better mixed than *pb, so use *pc first. If you want + * a 64-bit value do something like "*pc + (((uint64_t)*pb)<<32)". + *) +procedure HashLittle2( + AKey: Pointer; //* the key to hash */ + ALength: SizeInt; //* length of the key */ + var APrimaryHashAndInitVal: UInt32; //* IN: primary initval, OUT: primary hash */ + var ASecondaryHashAndInitVal: UInt32); //* IN: secondary initval, OUT: secondary hash */ +var + a,b,c: UInt32; + u: record case byte of + 0: (ptr: Pointer); + 1: (i: PtrUint); + end absolute AKey; + + k32: ^UInt32 absolute AKey; + k16: ^UInt16 absolute AKey; + k8: ^UInt8 absolute AKey; + +label _10, _8, _6, _4, _2; +label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1; + +begin + //* Set up the internal state */ + a := $DEADBEEF + UInt32(ALength) + APrimaryHashAndInitVal; + b := a; + c := b; + c += ASecondaryHashAndInitVal; + +{$IFDEF ENDIAN_LITTLE} + if (u.i and $3) = 0 then + begin + while (ALength > 12) do + begin + a += k32[0]; + b += k32[1]; + c += k32[2]; + mix_abc; + ALength -= 12; + k32 += 3; + end; + + case ALength of + 12: begin c += k32[2]; b += k32[1]; a += k32[0]; end; + 11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end; + 10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end; + 9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end; + 8 : begin b += k32[1]; a += k32[0]; end; + 7 : begin b += k32[1] and $ffffff; a += k32[0]; end; + 6 : begin b += k32[1] and $ffff; a += k32[0]; end; + 5 : begin b += k32[1] and $ff; a += k32[0]; end; + 4 : begin a += k32[0]; end; + 3 : begin a += k32[0] and $ffffff; end; + 2 : begin a += k32[0] and $ffff; end; + 1 : begin a += k32[0] and $ff; end; + 0 : + begin + APrimaryHashAndInitVal := c; + ASecondaryHashAndInitVal := b; + Exit; // zero length strings require no mixing + end; + end + end + else + if (u.i and $1) = 0 then + begin + while (ALength > 12) do + begin + a += k16[0] + (UInt32(k16[1]) shl 16); + b += k16[2] + (UInt32(k16[3]) shl 16); + c += k16[4] + (UInt32(k16[5]) shl 16); + mix_abc; + ALength -= 12; + k16 += 6; + end; + + case ALength of + 12: + begin + c+=k16[4]+((UInt32(k16[5])) shl 16); + b+=k16[2]+((UInt32(k16[3])) shl 16); + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 11: + begin + c+=(UInt32(k8[10])) shl 16; //* fall through */ + goto _10; + end; + 10: + begin _10: + c+=k16[4]; + b+=k16[2]+((UInt32(k16[3])) shl 16); + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 9 : + begin + c+=k8[8]; //* fall through */ + goto _8; + end; + 8 : + begin _8: + b+=k16[2]+((UInt32(k16[3])) shl 16); + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 7 : + begin + b+=(UInt32(k8[6])) shl 16; //* fall through */ + goto _6; + end; + 6 : + begin _6: + b+=k16[2]; + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 5 : + begin + b+=k8[4]; //* fall through */ + goto _4; + end; + 4 : + begin _4: + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 3 : + begin + a+=(UInt32(k8[2])) shl 16; //* fall through */ + goto _2; + end; + 2 : + begin _2: + a+=k16[0]; + end; + 1 : + begin + a+=k8[0]; + end; + 0 : + begin + APrimaryHashAndInitVal := c; + ASecondaryHashAndInitVal := b; + Exit; // zero length strings require no mixing + end; + end; + end + else +{$ENDIF} + begin + while ALength > 12 do + begin + a += k8[0]; + a += (UInt32(k8[1])) shl 8; + a += (UInt32(k8[2])) shl 16; + a += (UInt32(k8[3])) shl 24; + b += k8[4]; + b += (UInt32(k8[5])) shl 8; + b += (UInt32(k8[6])) shl 16; + b += (UInt32(k8[7])) shl 24; + c += k8[8]; + c += (UInt32(k8[9])) shl 8; + c += (UInt32(k8[10])) shl 16; + c += (UInt32(k8[11])) shl 24; + mix_abc; + ALength -= 12; + k8 += 12; + end; + + case ALength of + 12: goto Case12; + 11: goto Case11; + 10: goto Case10; + 9 : goto Case9; + 8 : goto Case8; + 7 : goto Case7; + 6 : goto Case6; + 5 : goto Case5; + 4 : goto Case4; + 3 : goto Case3; + 2 : goto Case2; + 1 : goto Case1; + 0 : + begin + APrimaryHashAndInitVal := c; + ASecondaryHashAndInitVal := b; + Exit; // zero length strings require no mixing + end; + end; + + Case12: c+=(UInt32(k8[11])) shl 24; + Case11: c+=(UInt32(k8[10])) shl 16; + Case10: c+=(UInt32(k8[9])) shl 8; + Case9: c+=k8[8]; + Case8: b+=(UInt32(k8[7])) shl 24; + Case7: b+=(UInt32(k8[6])) shl 16; + Case6: b+=(UInt32(k8[5])) shl 8; + Case5: b+=k8[4]; + Case4: a+=(UInt32(k8[3])) shl 24; + Case3: a+=(UInt32(k8[2])) shl 16; + Case2: a+=(UInt32(k8[1])) shl 8; + Case1: a+=k8[0]; + end; + + final_abc; + APrimaryHashAndInitVal := c; + ASecondaryHashAndInitVal := b; +end; + +procedure DelphiHashLittle2(AKey: Pointer; ALength: SizeInt; var APrimaryHashAndInitVal, ASecondaryHashAndInitVal: UInt32); +var + a,b,c: UInt32; + u: record case byte of + 0: (ptr: Pointer); + 1: (i: PtrUint); + end absolute AKey; + + k32: ^UInt32 absolute AKey; + k16: ^UInt16 absolute AKey; + k8: ^UInt8 absolute AKey; + +label _10, _8, _6, _4, _2; +label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1; + +begin + //* Set up the internal state */ + a := $DEADBEEF + UInt32(ALength shl 2) + APrimaryHashAndInitVal; // delphi version bug? original version don't have "shl 2" + b := a; + c := b; + c += ASecondaryHashAndInitVal; + +{$IFDEF ENDIAN_LITTLE} + if (u.i and $3) = 0 then + begin + while (ALength > 12) do + begin + a += k32[0]; + b += k32[1]; + c += k32[2]; + mix_abc; + ALength -= 12; + k32 += 3; + end; + + case ALength of + 12: begin c += k32[2]; b += k32[1]; a += k32[0]; end; + 11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end; + 10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end; + 9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end; + 8 : begin b += k32[1]; a += k32[0]; end; + 7 : begin b += k32[1] and $ffffff; a += k32[0]; end; + 6 : begin b += k32[1] and $ffff; a += k32[0]; end; + 5 : begin b += k32[1] and $ff; a += k32[0]; end; + 4 : begin a += k32[0]; end; + 3 : begin a += k32[0] and $ffffff; end; + 2 : begin a += k32[0] and $ffff; end; + 1 : begin a += k32[0] and $ff; end; + 0 : + begin + APrimaryHashAndInitVal := c; + ASecondaryHashAndInitVal := b; + Exit; // zero length strings require no mixing + end; + end + end + else + if (u.i and $1) = 0 then + begin + while (ALength > 12) do + begin + a += k16[0] + (UInt32(k16[1]) shl 16); + b += k16[2] + (UInt32(k16[3]) shl 16); + c += k16[4] + (UInt32(k16[5]) shl 16); + mix_abc; + ALength -= 12; + k16 += 6; + end; + + case ALength of + 12: + begin + c+=k16[4]+((UInt32(k16[5])) shl 16); + b+=k16[2]+((UInt32(k16[3])) shl 16); + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 11: + begin + c+=(UInt32(k8[10])) shl 16; //* fall through */ + goto _10; + end; + 10: + begin _10: + c+=k16[4]; + b+=k16[2]+((UInt32(k16[3])) shl 16); + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 9 : + begin + c+=k8[8]; //* fall through */ + goto _8; + end; + 8 : + begin _8: + b+=k16[2]+((UInt32(k16[3])) shl 16); + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 7 : + begin + b+=(UInt32(k8[6])) shl 16; //* fall through */ + goto _6; + end; + 6 : + begin _6: + b+=k16[2]; + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 5 : + begin + b+=k8[4]; //* fall through */ + goto _4; + end; + 4 : + begin _4: + a+=k16[0]+((UInt32(k16[1])) shl 16); + end; + 3 : + begin + a+=(UInt32(k8[2])) shl 16; //* fall through */ + goto _2; + end; + 2 : + begin _2: + a+=k16[0]; + end; + 1 : + begin + a+=k8[0]; + end; + 0 : + begin + APrimaryHashAndInitVal := c; + ASecondaryHashAndInitVal := b; + Exit; // zero length strings require no mixing + end; + end; + end + else +{$ENDIF} + begin + while ALength > 12 do + begin + a += k8[0]; + a += (UInt32(k8[1])) shl 8; + a += (UInt32(k8[2])) shl 16; + a += (UInt32(k8[3])) shl 24; + b += k8[4]; + b += (UInt32(k8[5])) shl 8; + b += (UInt32(k8[6])) shl 16; + b += (UInt32(k8[7])) shl 24; + c += k8[8]; + c += (UInt32(k8[9])) shl 8; + c += (UInt32(k8[10])) shl 16; + c += (UInt32(k8[11])) shl 24; + mix_abc; + ALength -= 12; + k8 += 12; + end; + + case ALength of + 12: goto Case12; + 11: goto Case11; + 10: goto Case10; + 9 : goto Case9; + 8 : goto Case8; + 7 : goto Case7; + 6 : goto Case6; + 5 : goto Case5; + 4 : goto Case4; + 3 : goto Case3; + 2 : goto Case2; + 1 : goto Case1; + 0 : + begin + APrimaryHashAndInitVal := c; + ASecondaryHashAndInitVal := b; + Exit; // zero length strings require no mixing + end; + end; + + Case12: c+=(UInt32(k8[11])) shl 24; + Case11: c+=(UInt32(k8[10])) shl 16; + Case10: c+=(UInt32(k8[9])) shl 8; + Case9: c+=k8[8]; + Case8: b+=(UInt32(k8[7])) shl 24; + Case7: b+=(UInt32(k8[6])) shl 16; + Case6: b+=(UInt32(k8[5])) shl 8; + Case5: b+=k8[4]; + Case4: a+=(UInt32(k8[3])) shl 24; + Case3: a+=(UInt32(k8[2])) shl 16; + Case2: a+=(UInt32(k8[1])) shl 8; + Case1: a+=k8[0]; + end; + + final_abc; + APrimaryHashAndInitVal := c; + ASecondaryHashAndInitVal := b; +end; + +function DelphiHashLittle(AKey: Pointer; ALength: SizeInt; AInitVal: UInt32): Int32; +var + a, b, c: UInt32; + u: record case byte of + 0: (ptr: Pointer); + 1: (i: PtrUint); + end absolute AKey; + + k32: ^UInt32 absolute AKey; + //k16: ^UInt16 absolute AKey; + k8: ^UInt8 absolute AKey; + +label Case12, Case11, Case10, Case9, Case8, Case7, Case6, Case5, Case4, Case3, Case2, Case1; + +begin + a := $DEADBEEF + UInt32(ALength shl 2) + AInitVal; // delphi version bug? original version don't have "shl 2" + b := a; + c := b; + +{.$IFDEF ENDIAN_LITTLE} // Delphi version don't care + if (u.i and $3) = 0 then + begin + while (ALength > 12) do + begin + a += k32[0]; + b += k32[1]; + c += k32[2]; + mix_abc; + ALength -= 12; + k32 += 3; + end; + + case ALength of + 12: begin c += k32[2]; b += k32[1]; a += k32[0]; end; + 11: begin c += k32[2] and $ffffff; b += k32[1]; a += k32[0]; end; + 10: begin c += k32[2] and $ffff; b += k32[1]; a += k32[0]; end; + 9 : begin c += k32[2] and $ff; b += k32[1]; a += k32[0]; end; + 8 : begin b += k32[1]; a += k32[0]; end; + 7 : begin b += k32[1] and $ffffff; a += k32[0]; end; + 6 : begin b += k32[1] and $ffff; a += k32[0]; end; + 5 : begin b += k32[1] and $ff; a += k32[0]; end; + 4 : begin a += k32[0]; end; + 3 : begin a += k32[0] and $ffffff; end; + 2 : begin a += k32[0] and $ffff; end; + 1 : begin a += k32[0] and $ff; end; + 0 : Exit(c); // zero length strings require no mixing + end + end + else +{.$ENDIF} + begin + while ALength > 12 do + begin + a += k8[0]; + a += (UInt32(k8[1])) shl 8; + a += (UInt32(k8[2])) shl 16; + a += (UInt32(k8[3])) shl 24; + b += k8[4]; + b += (UInt32(k8[5])) shl 8; + b += (UInt32(k8[6])) shl 16; + b += (UInt32(k8[7])) shl 24; + c += k8[8]; + c += (UInt32(k8[9])) shl 8; + c += (UInt32(k8[10])) shl 16; + c += (UInt32(k8[11])) shl 24; + mix_abc; + ALength -= 12; + k8 += 12; + end; + + case ALength of + 12: goto Case12; + 11: goto Case11; + 10: goto Case10; + 9 : goto Case9; + 8 : goto Case8; + 7 : goto Case7; + 6 : goto Case6; + 5 : goto Case5; + 4 : goto Case4; + 3 : goto Case3; + 2 : goto Case2; + 1 : goto Case1; + 0 : Exit(c); + end; + + Case12: c+=(UInt32(k8[11])) shl 24; + Case11: c+=(UInt32(k8[10])) shl 16; + Case10: c+=(UInt32(k8[9])) shl 8; + Case9: c+=k8[8]; + Case8: b+=(UInt32(k8[7])) shl 24; + Case7: b+=(UInt32(k8[6])) shl 16; + Case6: b+=(UInt32(k8[5])) shl 8; + Case5: b+=k8[4]; + Case4: a+=(UInt32(k8[3])) shl 24; + Case3: a+=(UInt32(k8[2])) shl 16; + Case2: a+=(UInt32(k8[1])) shl 8; + Case1: a+=k8[0]; + end; + + final_abc; + Result := Int32(c); +end; + +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.helpers.pas b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.helpers.pas new file mode 100644 index 000000000..72ddab3f3 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.helpers.pas @@ -0,0 +1,157 @@ +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2014 by Maciej Izak (hnb) + member of the Free Sparta development team (http://freesparta.com) + + Copyright(c) 2004-2014 DaThoX + + It contains the Free Pascal generics library + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} + +unit Generics.Helpers; + +{$MODE DELPHI}{$H+} +{$MODESWITCH TYPEHELPERS} + +interface + +uses + Classes, SysUtils; + +type + { TValueAnsiStringHelper } + + TValueAnsiStringHelper = record helper for AnsiString + function ToLower: AnsiString; inline; + end; + + { TValuewideStringHelper } + + TValueWideStringHelper = record helper for WideString + function ToLower: WideString; inline; + end; + + { TValueUnicodeStringHelper } + + TValueUnicodeStringHelper = record helper for UnicodeString + function ToLower: UnicodeString; inline; + end; + + { TValueShortStringHelper } + + TValueShortStringHelper = record helper for ShortString + function ToLower: ShortString; inline; + end; + + { TValueUTF8StringHelper } + + TValueUTF8StringHelper = record helper for UTF8String + function ToLower: UTF8String; inline; + end; + + { TValueRawByteStringHelper } + + TValueRawByteStringHelper = record helper for RawByteString + function ToLower: RawByteString; inline; + end; + + { TValueUInt32Helper } + + TValueUInt32Helper = record helper for UInt32 + function High: LongInt; inline; + function Low: LongInt; inline; + + class function GetSignMask: UInt32; static; inline; + class function GetSizedSignMask(ABits: Byte): UInt32; static; inline; + class function GetBitsLength: Byte; static; inline; + + const + SIZED_SIGN_MASK: array[1..32] of UInt32 = ( + $80000000, $C0000000, $E0000000, $F0000000, $F8000000, $FC000000, $FE000000, $FF000000, + $FF800000, $FFC00000, $FFE00000, $FFF00000, $FFF80000, $FFFC0000, $FFFE0000, $FFFF0000, + $FFFF8000, $FFFFC000, $FFFFE000, $FFFFF000, $FFFFF800, $FFFFFC00, $FFFFFE00, $FFFFFF00, + $FFFFFF80, $FFFFFFC0, $FFFFFFE0, $FFFFFFF0, $FFFFFFF8, $FFFFFFFC, $FFFFFFFE, $FFFFFFFF); + BITS_LENGTH = 32; + end; + +implementation + +{ TRawDataStringHelper } + +function TValueAnsiStringHelper.ToLower: AnsiString; +begin + Result := LowerCase(Self); +end; + +{ TValueWideStringHelper } + +function TValueWideStringHelper.ToLower: WideString; +begin + Result := LowerCase(Self); +end; + +{ TValueUnicodeStringHelper } + +function TValueUnicodeStringHelper.ToLower: UnicodeString; +begin + Result := LowerCase(Self); +end; + +{ TValueShortStringHelper } + +function TValueShortStringHelper.ToLower: ShortString; +begin + Result := LowerCase(Self); +end; + +{ TValueUTF8StringHelper } + +function TValueUTF8StringHelper.ToLower: UTF8String; +begin + Result := LowerCase(Self); +end; + +{ TValueRawByteStringHelper } + +function TValueRawByteStringHelper.ToLower: RawByteString; +begin + Result := LowerCase(Self); +end; + +{ TValueUInt32Helper } + +function TValueUInt32Helper.High: LongInt; +begin + Result := System.High(UInt32); +end; + +function TValueUInt32Helper.Low: LongInt; +begin + Result := System.Low(UInt32); +end; + +class function TValueUInt32Helper.GetSignMask: UInt32; +begin + Result := $80000000; +end; + +class function TValueUInt32Helper.GetSizedSignMask(ABits: Byte): UInt32; +begin + Result := SIZED_SIGN_MASK[ABits]; +end; + +class function TValueUInt32Helper.GetBitsLength: Byte; +begin + Result := BITS_LENGTH; +end; + +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.memoryexpanders.pas b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.memoryexpanders.pas new file mode 100644 index 000000000..11167a998 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.memoryexpanders.pas @@ -0,0 +1,236 @@ +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2014 by Maciej Izak (hnb) + member of the Free Sparta development team (http://freesparta.com) + + Copyright(c) 2004-2014 DaThoX + + It contains the Free Pascal generics library + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} + +unit Generics.MemoryExpanders; +// Memory expanders + +{$mode delphi} +{$MACRO ON} +{.$WARN 5024 OFF} +{.$WARN 4079 OFF} + +interface + +uses + Classes, SysUtils; + +type + TProbeSequence = class + public + end; + + { TLinearProbing } + + TLinearProbing = class(TProbeSequence) + public + class function Probe(I, {%H-}M, Hash: UInt32): UInt32; static; inline; + + const MAX_LOAD_FACTOR = 1; + const DEFAULT_LOAD_FACTOR = 0.75; + end; + + { TQuadraticProbing } + + TQuadraticProbing = class(TProbeSequence) + private + class constructor Create; + public + class var C1: UInt32; + class var C2: UInt32; + + class function Probe(I, {%H-}M, Hash: UInt32): UInt32; static; inline; + + const MAX_LOAD_FACTOR = 0.5; + const DEFAULT_LOAD_FACTOR = 0.5; + end; + + { TDoubleHashing } + + TDoubleHashing = class(TProbeSequence) + public + class function Probe(I, {%H-}M, Hash1: UInt32; Hash2: UInt32 = 1): UInt32; static; inline; + + const MAX_LOAD_FACTOR = 1; + const DEFAULT_LOAD_FACTOR = 0.85; + end; + +const + // http://stackoverflow.com/questions/757059/position-of-least-significant-bit-that-is-set + // MultiplyDeBruijnBitPosition[uint32(((numberInt32 and -numberInt32) * $077CB531)) shr 27] + MultiplyDeBruijnBitPosition: array[0..31] of Int32 = + ( + 0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8, + 31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9 + ); + + // http://primes.utm.edu/lists/2small/0bit.html + // http://www.math.niu.edu/~rusin/known-math/98/pi_x + // http://oeis.org/A014234/ + PrimaryNumbersJustLessThanPowerOfTwo: array[0..31] of UInt32 = + ( + 0, 1, 3, 7, 13, 31, 61, 127, 251, 509, 1021, 2039, 4093, 8191, 16381, 32749, 65521, 131071, + 262139, 524287, 1048573, 2097143, 4194301, 8388593, 16777213, 33554393, 67108859, + 134217689, 268435399, 536870909, 1073741789, 2147483647 + ); + + // http://oeis.org/A014210 + // http://oeis.org/A203074 + PrimaryNumbersJustBiggerThanPowerOfTwo: array[0..31] of UInt32 = ( + 2,3,5,11,17,37,67,131,257,521,1031,2053,4099, + 8209,16411,32771,65537,131101,262147,524309, + 1048583,2097169,4194319,8388617,16777259,33554467, + 67108879,134217757,268435459,536870923,1073741827, + 2147483659); + + // Fibonacci numbers + FibonacciNumbers: array[0..44] of UInt32 = ( + {0,1,1,2,3,}0,5,8,13,21,34,55,89,144,233,377,610,987, + 1597,2584,4181,6765,10946,17711,28657,46368,75025, + 121393,196418,317811,514229,832040,1346269, + 2178309,3524578,5702887,9227465,14930352,24157817, + 39088169, 63245986, 102334155, 165580141, 267914296, + 433494437, 701408733, 1134903170, 1836311903, 2971215073, + {! not fib number - this is memory limit} 4294967295); + + // Largest prime not exceeding Fibonacci(n) + // http://oeis.org/A138184/list + // http://www.numberempire.com/primenumbers.php + PrimaryNumbersJustLessThanFibonacciNumbers: array[0..44] of UInt32 = ( + {! not correlated to fib number. For empty table} 0, + 5,7,13,19,31,53,89,139,233,373,607,983,1597, + 2579,4177,6763,10939,17707,28657,46351,75017, + 121379,196387,317797,514229,832003,1346249, + 2178283,3524569,5702867,9227443,14930341,24157811, + 39088157,63245971,102334123,165580123,267914279, + 433494437,701408717,1134903127,1836311879,2971215073, + {! not correlated to fib number - this is prime memory limit} 4294967291); + + // Smallest prime >= n-th Fibonacci number. + // http://oeis.org/A138185 + PrimaryNumbersJustBiggerThanFibonacciNumbers: array[0..44] of UInt32 = ( + {! not correlated to fib number. For empty table} 0, + 5,11,13,23,37,59,89,149,233,379,613, + 991,1597,2591,4201,6779,10949,17713,28657,46381, + 75029,121403,196429,317827,514229,832063,1346273, + 2178313,3524603,5702897,9227479,14930387,24157823, + 39088193,63245989,102334157,165580147,267914303, + 433494437,701408753,1134903179,1836311951,2971215073, + {! not correlated to fib number - this is prime memory limit} 4294967291); + +type + + { TCuckooHashingCfg } + + TCuckooHashingCfg = class + public + const D = 2; + const MAX_LOAD_FACTOR = 0.5; + + class function LoadFactor(M: Integer): Integer; virtual; + end; + + TStdCuckooHashingCfg = class(TCuckooHashingCfg) + public + const MAX_LOOP = 1000; + end; + + TDeamortizedCuckooHashingCfg = class(TCuckooHashingCfg) + public + const L = 5; + end; + + TDeamortizedCuckooHashingCfg_D2 = TDeamortizedCuckooHashingCfg; + + { TDeamortizedCuckooHashingCfg_D4 } + + TDeamortizedCuckooHashingCfg_D4 = class(TDeamortizedCuckooHashingCfg) + public + const D = 4; + const L = 20; + const MAX_LOAD_FACTOR = 0.9; + + class function LoadFactor(M: Integer): Integer; override; + end; + + { TDeamortizedCuckooHashingCfg_D6 } + + TDeamortizedCuckooHashingCfg_D6 = class(TDeamortizedCuckooHashingCfg) + public + const D = 6; + const L = 170; + const MAX_LOAD_FACTOR = 0.99; + + class function LoadFactor(M: Integer): Integer; override; + end; + + TL5CuckooHashingCfg = class(TCuckooHashingCfg) + public + end; + +implementation + +{ TDeamortizedCuckooHashingCfg_D6 } + +class function TDeamortizedCuckooHashingCfg_D6.LoadFactor(M: Integer): Integer; +begin + Result:=Pred(Round(MAX_LOAD_FACTOR*M)); +end; + +{ TDeamortizedCuckooHashingCfg_D4 } + +class function TDeamortizedCuckooHashingCfg_D4.LoadFactor(M: Integer): Integer; +begin + Result:=Pred(Round(MAX_LOAD_FACTOR*M)); +end; + +{ TCuckooHashingCfg } + +class function TCuckooHashingCfg.LoadFactor(M: Integer): Integer; +begin + Result := Pred(M shr 1); +end; + +{ TLinearProbing } + +class function TLinearProbing.Probe(I, M, Hash: UInt32): UInt32; +begin + Result := (Hash + I) +end; + +{ TQuadraticProbing } + +class constructor TQuadraticProbing.Create; +begin + C1 := 1; + C2 := 1; +end; + +class function TQuadraticProbing.Probe(I, M, Hash: UInt32): UInt32; +begin + Result := (Hash + C1 * I {%H-}+ C2 * Sqr(I)); +end; + +{ TDoubleHashingNoMod } + +class function TDoubleHashing.Probe(I, M, Hash1: UInt32; Hash2: UInt32): UInt32; +begin + Result := Hash1 + I * Hash2; +end; + +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.strings.pas b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.strings.pas new file mode 100644 index 000000000..1f9c2690e --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/generics.strings.pas @@ -0,0 +1,34 @@ +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2014 by Maciej Izak (hnb) + member of the Free Sparta development team (http://freesparta.com) + + Copyright(c) 2004-2014 DaThoX + + It contains the Free Pascal generics library + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} + +unit Generics.Strings; + +{$mode objfpc}{$H+} + +interface + +resourcestring + SArgumentOutOfRange = 'Argument out of range'; + SDuplicatesNotAllowed = 'Duplicates not allowed in dictionary'; + SDictionaryKeyDoesNotExist = 'Dictionary key does not exist'; + SItemNotFound = 'Item not found'; + +implementation + +end. + diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/inc/generics.dictionaries.inc b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/inc/generics.dictionaries.inc new file mode 100644 index 000000000..74820d755 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/inc/generics.dictionaries.inc @@ -0,0 +1,1859 @@ +{%MainUnit generics.collections.pas} + +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2014 by Maciej Izak (hnb) + member of the Free Sparta development team (http://freesparta.com) + + Copyright(c) 2004-2014 DaThoX + + It contains the Free Pascal generics library + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} + +{ TPair<TKey,TValue> } + +class function TPair<TKey, TValue>.Create(AKey: TKey; + AValue: TValue): TPair<TKey, TValue>; +begin + Result.Key := AKey; + Result.Value := AValue; +end; + +{ TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS> } + +procedure TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.PairNotify(constref APair: TPair<TKey, TValue>; + ACollectionNotification: TCollectionNotification); +begin + KeyNotify(APair.Key, ACollectionNotification); + ValueNotify(APair.Value, ACollectionNotification); +end; + +procedure TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.KeyNotify(constref AKey: TKey; + ACollectionNotification: TCollectionNotification); +begin + if Assigned(FOnKeyNotify) then + FOnKeyNotify(Self, AKey, ACollectionNotification); +end; + +procedure TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.SetValue(var AValue: TValue; constref ANewValue: TValue); +var + LOldValue: TValue; +begin + LOldValue := AValue; + AValue := ANewValue; + + ValueNotify(LOldValue, cnRemoved); + ValueNotify(ANewValue, cnAdded); +end; + +procedure TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.ValueNotify(constref AValue: TValue; + ACollectionNotification: TCollectionNotification); +begin + if Assigned(FOnValueNotify) then + FOnValueNotify(Self, AValue, ACollectionNotification); +end; + +constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create; +begin + Create(0); +end; + +constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACapacity: SizeInt); overload; +begin + Create(ACapacity, TEqualityComparer<TKey>.Default(THashFactory)); +end; + +constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACapacity: SizeInt; + const AComparer: IEqualityComparer<TKey>); +begin + FEqualityComparer := AComparer; + SetCapacity(ACapacity); +end; + +constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(const AComparer: IEqualityComparer<TKey>); +begin + Create(0, AComparer); +end; + +constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>); +begin + Create(ACollection, TEqualityComparer<TKey>.Default(THashFactory)); +end; + +constructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>; + const AComparer: IEqualityComparer<TKey>); overload; +var + LItem: TPair<TKey, TValue>; +begin + Create(AComparer); + for LItem in ACollection do + Add(LItem); +end; + +destructor TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.Destroy; +begin + Clear; + FKeys.Free; + FValues.Free; + inherited; +end; + +function TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.ToArray(ACount: SizeInt): TArray<TDictionaryPair>; +var + i: SizeInt; + LEnumerator: TEnumerator<TDictionaryPair>; +begin + SetLength(Result, ACount); + LEnumerator := DoGetEnumerator; + + i := 0; + while LEnumerator.MoveNext do + begin + Result[i] := LEnumerator.Current; + Inc(i); + end; + LEnumerator.Free; +end; + +function TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>.ToArray: TArray<TDictionaryPair>; +begin + Result := ToArray(Count); +end; + +{ TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS> } + +constructor TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>.Create( + ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>); +begin + inherited Create; + FIndex := -1; + FDictionary := ADictionary; +end; + +function TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>.DoGetCurrent: T; +begin + Result := GetCurrent; +end; + +{ TDictionaryEnumerable<TDictionaryEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS> } + +constructor TDictionaryEnumerable<TDictionaryEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.Create( + ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>); +begin + FDictionary := ADictionary; +end; + +function TDictionaryEnumerable<TDictionaryEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>. + DoGetEnumerator: TDictionaryEnumerator; +begin + Result := TDictionaryEnumerator(TDictionaryEnumerator.NewInstance); + TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>(Result).Create(FDictionary); +end; + +function TDictionaryEnumerable<TDictionaryEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.GetCount: SizeInt; +begin + Result := TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>(FDictionary).Count; +end; + +function TDictionaryEnumerable<TDictionaryEnumerator, T, CUSTOM_DICTIONARY_CONSTRAINTS>.ToArray: TArray; +begin + Result := ToArrayImpl(FDictionary.Count); +end; + +{ TOpenAddressingEnumerator<T, DICTIONARY_CONSTRAINTS> } + +function TOpenAddressingEnumerator<T, OPEN_ADDRESSING_CONSTRAINTS>.DoMoveNext: Boolean; +var + LLength: SizeInt; +begin + Inc(FIndex); + + LLength := Length(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems); + + if FIndex >= LLength then + Exit(False); + + // maybe related to bug #24098 + // compiler error for (TDictionary<DICTIONARY_CONSTRAINTS>(FDictionary).FItems[FIndex].Hash and UInt32.GetSignMask) = 0 + while ((TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Hash) and UInt32.GetSignMask) = 0 do + begin + Inc(FIndex); + if FIndex = LLength then + Exit(False); + end; + + Result := True; +end; + +{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS> } + +constructor TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt; + const AComparer: IEqualityComparer<TKey>); +begin + inherited Create(ACapacity, AComparer); + + FMaxLoadFactor := TProbeSequence.DEFAULT_LOAD_FACTOR; +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetKeys: TKeyCollection; +begin + if not Assigned(FKeys) then + FKeys := TKeyCollection.Create(Self); + Result := TKeyCollection(FKeys); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetValues: TValueCollection; +begin + if not Assigned(FValues) then + FValues := TValueCollection.Create(Self); + Result := TValueCollection(FValues); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndex(constref AKey: TKey): SizeInt; +var + LHash: UInt32; +begin + Result := FindBucketIndex(FItems, AKey, LHash); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.PrepareAddingItem: SizeInt; +begin + if RealItemsLength > FItemsThreshold then + Rehash(Length(FItems) shl 1) + else if FItemsThreshold = 0 then + begin + SetLength(FItems, 8); + UpdateItemsThreshold(8); + end + else if FItemsLength = $40000001 then // High(TIndex) ... Error: Type mismatch + OutOfMemoryError; + + Result := FItemsLength; + Inc(FItemsLength); +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt); +begin + if ASize = $40000000 then + FItemsThreshold := $40000001 + else + FItemsThreshold := Pred(Round(ASize * FMaxLoadFactor)); +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.AddItem(var AItem: TItem; constref AKey: TKey; + constref AValue: TValue; const AHash: UInt32); +begin + AItem.Hash := AHash; + AItem.Pair.Key := AKey; + AItem.Pair.Value := AValue; + + PairNotify(AItem.Pair, cnAdded); +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Add(constref AKey: TKey; constref AValue: TValue); +begin + DoAdd(AKey, AValue); +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Add(constref APair: TPair<TKey, TValue>); +begin + DoAdd(APair.Key, APair.Value); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.DoAdd(constref AKey: TKey; constref AValue: TValue): SizeInt; +var + LHash: UInt32; +begin + PrepareAddingItem; + + Result := FindBucketIndex(FItems, AKey, LHash); + if Result >= 0 then + raise EListError.CreateRes(@SDuplicatesNotAllowed); + + Result := not Result; + AddItem(FItems[Result], AKey, AValue, LHash); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.DoRemove(AIndex: SizeInt; + ACollectionNotification: TCollectionNotification): TValue; +var + LItem: PItem; + LPair: TPair<TKey, TValue>; +begin + LItem := @FItems[AIndex]; + LItem.Hash := 0; + Result := LItem.Pair.Value; + LPair := LItem.Pair; + LItem.Pair := Default(TPair<TKey, TValue>); + Dec(FItemsLength); + PairNotify(LPair, ACollectionNotification); +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Remove(constref AKey: TKey); +var + LIndex: SizeInt; +begin + LIndex := FindBucketIndex(AKey); + if LIndex < 0 then + Exit; + + DoRemove(LIndex, cnRemoved); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.ExtractPair(constref AKey: TKey): TPair<TKey, TValue>; +var + LIndex: SizeInt; +begin + LIndex := FindBucketIndex(AKey); + if LIndex < 0 then + Exit(Default(TPair<TKey, TValue>)); + + Result.Key := AKey; + Result.Value := DoRemove(LIndex, cnExtracted); +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Clear; +var + LItem: PItem; + i: SizeInt; + LOldItems: array of TItem; +begin + FItemsLength := 0; + FItemsThreshold := 0; + // ClearTombstones; + LOldItems := FItems; + FItems := nil; + + for i := 0 to High(LOldItems) do + begin + LItem := @LOldItems[i]; + if (LItem.Hash and UInt32.GetSignMask = 0) then + Continue; + + PairNotify(LItem.Pair, cnRemoved); + end; +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.RealItemsLength: SizeInt; +begin + Result := FItemsLength; +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Rehash(ASizePow2: SizeInt; AForce: Boolean): Boolean; +var + LNewItems: TArray<TItem>; + LHash: UInt32; + LIndex: SizeInt; + i: SizeInt; + LItem, LNewItem: PItem; +begin + if (ASizePow2 = Length(FItems)) and not AForce then + Exit(False); + if ASizePow2 < 0 then + OutOfMemoryError; + + SetLength(LNewItems, ASizePow2); + UpdateItemsThreshold(ASizePow2); + + for i := 0 to High(FItems) do + begin + LItem := @FItems[i]; + + if (LItem.Hash and UInt32.GetSignMask) <> 0 then + begin + LIndex := FindBucketIndex(LNewItems, LItem.Pair.Key, LHash); + LIndex := not LIndex; + + LNewItem := @LNewItems[LIndex]; + LNewItem.Hash := LHash; + LNewItem.Pair := LItem.Pair; + end; + end; + + FItems := LNewItems; + Result := True; +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.DoGetEnumerator: TEnumerator<TDictionaryPair>; +begin + Result := GetEnumerator; +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.SetCapacity(ACapacity: SizeInt); +begin + if ACapacity < FItemsLength then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + Resize(ACapacity); +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.SetMaxLoadFactor(AValue: single); +var + LItemsLength: SizeInt; +begin + if (AValue > TProbeSequence.MAX_LOAD_FACTOR) or (AValue <= 0) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + FMaxLoadFactor := AValue; + + repeat + LItemsLength := Length(FItems); + UpdateItemsThreshold(LItemsLength); + if RealItemsLength > FItemsThreshold then + Rehash(LItemsLength shl 1); + until RealItemsLength <= FItemsThreshold; +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetLoadFactor: single; +begin + Result := FItemsLength / Length(FItems); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetCapacity: SizeInt; +begin + Result := Length(FItems); +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.Resize(ANewSize: SizeInt); +var + LNewSize: SizeInt; +begin + if ANewSize < 0 then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + LNewSize := 0; + if ANewSize > 0 then + begin + LNewSize := 8; + while LNewSize < ANewSize do + LNewSize := LNewSize shl 1; + end; + + Rehash(LNewSize); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetEnumerator: TPairEnumerator; +begin + Result := TPairEnumerator.Create(Self); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetItem(const AKey: TKey): TValue; +var + LIndex: SizeInt; +begin + LIndex := FindBucketIndex(AKey); + if LIndex < 0 then + raise EListError.CreateRes(@SDictionaryKeyDoesNotExist); + Result := FItems[LIndex].Pair.Value; +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TrimExcess; +begin + SetCapacity(Succ(FItemsLength)); +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.SetItem(const AKey: TKey; const AValue: TValue); +var + LIndex: SizeInt; +begin + LIndex := FindBucketIndex(AKey); + if LIndex < 0 then + raise EListError.CreateRes(@SItemNotFound); + + SetValue(FItems[LIndex].Pair.Value, AValue); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TryGetValue(constref AKey: TKey; out AValue: TValue): Boolean; +var + LIndex: SizeInt; +begin + LIndex := FindBucketIndex(AKey); + Result := LIndex >= 0; + + if Result then + AValue := FItems[LIndex].Pair.Value + else + AValue := Default(TValue); +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.AddOrSetValue(constref AKey: TKey; constref AValue: TValue); +var + LIndex: SizeInt; + LHash: UInt32; +begin + LIndex := FindBucketIndex(FItems, AKey, LHash); + + if LIndex < 0 then + DoAdd(AKey, AValue) + else + SetValue(FItems[LIndex].Pair.Value, AValue); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.ContainsKey(constref AKey: TKey): Boolean; +var + LIndex: SizeInt; +begin + LIndex := FindBucketIndex(AKey); + Result := LIndex >= 0; +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.ContainsValue(constref AValue: TValue): Boolean; +begin + Result := ContainsValue(AValue, TEqualityComparer<TValue>.Default(THashFactory)); +end; + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.ContainsValue(constref AValue: TValue; + const AEqualityComparer: IEqualityComparer<TValue>): Boolean; +var + i: SizeInt; + LItem: PItem; +begin + if Length(FItems) = 0 then + Exit(False); + + for i := 0 to High(FItems) do + begin + LItem := @FItems[i]; + if (LItem.Hash and UInt32.GetSignMask) = 0 then + Continue; + + if AEqualityComparer.Equals(AValue, LItem.Pair.Value) then + Exit(True); + end; + Result := False; +end; + +procedure TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.GetMemoryLayout( + const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition); +var + i: SizeInt; +begin + for i := 0 to High(FItems) do + if (FItems[i].Hash and UInt32.GetSignMask) <> 0 then + AOnGetMemoryLayoutKeyPosition(Self, i); +end; + +{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPairEnumerator } + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TPairEnumerator.GetCurrent: TPair<TKey, TValue>; +begin + Result := TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair; +end; + +{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TValueEnumerator } + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TValueEnumerator.GetCurrent: TValue; +begin + Result := TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair.Value; +end; + +{ TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TKeyEnumerator } + +function TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.TKeyEnumerator.GetCurrent: TKey; +begin + Result := TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>(FDictionary).FItems[FIndex].Pair.Key; +end; + +{ TOpenAddressingLP<DICTIONARY_CONSTRAINTS> } + +procedure TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.NotifyIndexChange(AFrom, ATo: SizeInt); +begin +end; + +function TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.DoRemove(AIndex: SizeInt; + ACollectionNotification: TCollectionNotification): TValue; +var + LItem: PItem; + LPair: TPair<TKey, TValue>; + LLengthMask: SizeInt; + i, m, LIndex, LGapIndex: SizeInt; + LHash, LBucket: UInt32; +begin + LItem := @FItems[AIndex]; + LPair := LItem.Pair; + + // try fill gap + LHash := LItem.Hash; + LItem.Hash := 0; // prevents an infinite searching loop + m := Length(FItems); + LLengthMask := m - 1; + i := Succ(AIndex - (LHash and LLengthMask)); + LGapIndex := AIndex; + repeat + LIndex := TProbeSequence.Probe(i, m, LHash) and LLengthMask; + LItem := @FItems[LIndex]; + + // Empty position + if (LItem.Hash and UInt32.GetSignMask) = 0 then + Break; // breaking bad! + + LBucket := LItem.Hash and LLengthMask; + if not InCircularRange(LGapIndex, LBucket, LIndex) then + begin + NotifyIndexChange(LIndex, LGapIndex); + FItems[LGapIndex] := LItem^; + LItem.Hash := 0; // new gap + LGapIndex := LIndex; + end; + Inc(i); + until false; + + LItem := @FItems[LGapIndex]; + LItem.Hash := 0; + LItem.Pair := Default(TPair<TKey, TValue>); + Dec(FItemsLength); + + Result := LPair.Value; + PairNotify(LPair, ACollectionNotification); +end; + +function TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndex(constref AItems: TArray<TItem>; + constref AKey: TKey; out AHash: UInt32): SizeInt; +var + LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613 + LLengthMask: SizeInt; + i, m: SizeInt; + LHash: UInt32; +begin + m := Length(AItems); + LLengthMask := m - 1; + + LHash := FEqualityComparer.GetHashCode(AKey); + + i := 0; + AHash := LHash or UInt32.GetSignMask; + + if m = 0 then + Exit(-1); + + Result := AHash and LLengthMask; + + repeat + LItem := _TItem(AItems[Result]); + + // Empty position + if (LItem.Hash and UInt32.GetSignMask) = 0 then + Exit(not Result); // insert! + + // Same position? + if LItem.Hash = AHash then + if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then + Exit; + + Inc(i); + + Result := TProbeSequence.Probe(i, m, AHash) and LLengthMask; + + until false; +end; + +{ TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS> } + +function TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.Rehash(ASizePow2: SizeInt; AForce: Boolean): Boolean; +begin + if inherited then + FTombstonesCount := 0; +end; + +function TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.RealItemsLength: SizeInt; +begin + Result := FItemsLength + FTombstonesCount +end; + +procedure TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.ClearTombstones; +begin + Rehash(Length(FItems), True); +end; + +procedure TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.Clear; +begin + FTombstonesCount := 0; + inherited; +end; + +function TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.DoRemove(AIndex: SizeInt; + ACollectionNotification: TCollectionNotification): TValue; +begin + Result := inherited; + + FItems[AIndex].Hash := 1; + Inc(FTombstonesCount); +end; + +function TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>.DoAdd(constref AKey: TKey; + constref AValue: TValue): SizeInt; +var + LHash: UInt32; +begin + PrepareAddingItem; + + Result := FindBucketIndexOrTombstone(FItems, AKey, LHash); + if Result >= 0 then + raise EListError.CreateRes(@SDuplicatesNotAllowed); + + Result := not Result; + // Can't ovverride because we lost info about old hash + if FItems[Result].Hash <> 0 then + Dec(FTombstonesCount); + + AddItem(FItems[Result], AKey, AValue, LHash); +end; + +{ TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS> } + +function TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndex(constref AItems: TArray<TItem>; + constref AKey: TKey; out AHash: UInt32): SizeInt; +var + LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613 + LLengthMask: SizeInt; + i, m: SizeInt; + LHash: UInt32; +begin + m := Length(AItems); + LLengthMask := m - 1; + + LHash := FEqualityComparer.GetHashCode(AKey); + + i := 0; + AHash := LHash or UInt32.GetSignMask; + + if m = 0 then + Exit(-1); + + Result := AHash and LLengthMask; + + repeat + LItem := _TItem(AItems[Result]); + // Empty position + if LItem.Hash = 0 then + Exit(not Result); // insert! + + // Same position? + if LItem.Hash = AHash then + if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then + Exit; + + Inc(i); + + Result := TProbeSequence.Probe(i, m, AHash) and LLengthMask; + + until false; +end; + +function TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; + constref AKey: TKey; out AHash: UInt32): SizeInt; +var + LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613 + LLengthMask: SizeInt; + i, m: SizeInt; + LHash: UInt32; +begin + m := Length(AItems); + LLengthMask := m - 1; + + LHash := FEqualityComparer.GetHashCode(AKey); + + i := 0; + AHash := LHash or UInt32.GetSignMask; + + if m = 0 then + Exit(-1); + + Result := AHash and LLengthMask; + + repeat + LItem := _TItem(AItems[Result]); + + // Empty position or tombstone + if LItem.Hash and UInt32.GetSignMask = 0 then + Exit(not Result); // insert! + + // Same position? + if LItem.Hash = AHash then + if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then + Exit; + + Inc(i); + + Result := TProbeSequence.Probe(i, m, AHash) and LLengthMask; + + until false; +end; + +{ TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS> } + +constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt; + const AComparer: IEqualityComparer<TKey>); +begin +end; + +constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(const AComparer: IEqualityComparer<TKey>); +begin +end; + +constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>; + const AComparer: IEqualityComparer<TKey>); +begin +end; + +constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt); +begin + Create(ACapacity, TExtendedEqualityComparer<TKey>.Default(THashFactory)); +end; + +constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>); +begin + Create(ACollection, TExtendedEqualityComparer<TKey>.Default(THashFactory)); +end; + +constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACapacity: SizeInt; + const AComparer: IExtendedEqualityComparer<TKey>); +begin + FMaxLoadFactor := TProbeSequence.DEFAULT_LOAD_FACTOR; + FEqualityComparer := AComparer; + SetCapacity(ACapacity); +end; + +constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(const AComparer: IExtendedEqualityComparer<TKey>); +begin + Create(0, AComparer); +end; + +constructor TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>; + const AComparer: IExtendedEqualityComparer<TKey>); +var + LItem: TPair<TKey, TValue>; +begin + Create(AComparer); + for LItem in ACollection do + Add(LItem); +end; + +procedure TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt); +begin + inherited; + R := + PrimaryNumbersJustLessThanPowerOfTwo[ + MultiplyDeBruijnBitPosition[UInt32(((ASize and -ASize) * $077CB531)) shr 27]] +end; + +function TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndex(constref AItems: TArray<TItem>; + constref AKey: TKey; out AHash: UInt32): SizeInt; +var + LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613 + LLengthMask: SizeInt; + i, m: SizeInt; + LHash: array[-1..1] of UInt32; + LHash1: UInt32 absolute LHash[0]; + LHash2: UInt32 absolute LHash[1]; +begin + m := Length(AItems); + LLengthMask := m - 1; + LHash[-1] := 2; // number of hashes + + IExtendedEqualityComparer<TKey>(FEqualityComparer).GetHashList(AKey, @LHash[-1]); + + i := 0; + AHash := LHash1 or UInt32.GetSignMask; + + if m = 0 then + Exit(-1); + + Result := LHash1 and LLengthMask; + // second hash function must be special + LHash2 := (R - (LHash2 mod R)) or 1; + + repeat + LItem := _TItem(AItems[Result]); + + // Empty position + if LItem.Hash = 0 then + Exit(not Result); + + // Same position? + if LItem.Hash = AHash then + if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then + Exit; + + Inc(i); + + Result := TProbeSequence.Probe(i, m, AHash, LHash2) and LLengthMask; + until false; +end; + +function TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS>.FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; + constref AKey: TKey; out AHash: UInt32): SizeInt; +var + LItem: {TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>.}_TItem; // for workaround Lazarus bug #25613 + LLengthMask: SizeInt; + i, m: SizeInt; + LHash: array[-1..1] of UInt32; + LHash1: UInt32 absolute LHash[0]; + LHash2: UInt32 absolute LHash[1]; +begin + m := Length(AItems); + LLengthMask := m - 1; + LHash[-1] := 2; // number of hashes + + IExtendedEqualityComparer<TKey>(FEqualityComparer).GetHashList(AKey, @LHash[-1]); + + i := 0; + AHash := LHash1 or UInt32.GetSignMask; + + if m = 0 then + Exit(-1); + + Result := LHash1 and LLengthMask; + // second hash function must be special + LHash2 := (R - (LHash2 mod R)) or 1; + + repeat + LItem := _TItem(AItems[Result]); + + // Empty position or tombstone + if LItem.Hash and UInt32.GetSignMask = 0 then + Exit(not Result); + + // Same position? + if LItem.Hash = AHash then + if FEqualityComparer.Equals(AKey, LItem.Pair.Key) then + Exit; + + Inc(i); + + Result := TProbeSequence.Probe(i, m, AHash, LHash2) and LLengthMask; + until false; +end; + +{ TDeamortizedDArrayCuckooMapEnumerator<T, CUCKOO_CONSTRAINTS> } + +constructor TDeamortizedDArrayCuckooMapEnumerator<T, CUCKOO_CONSTRAINTS>.Create( + ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>); +begin + inherited; + if ADictionary.Count = 0 then + FMainIndex := TCuckooCfg.D + else + FMainIndex := 0; +end; + +function TDeamortizedDArrayCuckooMapEnumerator<T, CUCKOO_CONSTRAINTS>.DoMoveNext: Boolean; +var + LLength: SizeInt; + LArray: TItemsArray; +begin + Inc(FIndex); + + if (FMainIndex = TCuckooCfg.D) then // queue + begin + LLength := Length(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems); + if FIndex >= LLength then + Exit(False); + + while ((TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Hash) + and UInt32.GetSignMask) = 0 do + begin + Inc(FIndex); + if FIndex = LLength then + Exit(False); + end; + end + else // d-array + begin + LArray := TItemsArray(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex]); + LLength := Length(LArray); + if FIndex >= LLength then + begin + Inc(FMainIndex); + FIndex := -1; + Exit(DoMoveNext); + end; + + while ((LArray[FIndex].Hash) and UInt32.GetSignMask) = 0 do + begin + Inc(FIndex); + if FIndex = LLength then + begin + Inc(FMainIndex); + FIndex := -1; + Exit(DoMoveNext); + end; + end; + end; + + Result := True; +end; + +{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS> } + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.Rehash(ASizePow2: SizeInt; + AForce: boolean): Boolean; +var + FOldIdx: array of TKey; + i: SizeInt; +begin + SetLength(FOldIdx, FIdx.Count); + for i := 0 to FIdx.Count - 1 do + FOldIdx[i] := FItems[FIdx[i]].Pair.Key; + + Result := inherited Rehash(ASizePow2, AForce); + + for i := 0 to FIdx.Count - 1 do + FIdx[i] := FindBucketIndex(FOldIdx[i]); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.NotifyIndexChange(AFrom, ATo: SizeInt); +var + i: SizeInt; +begin + // notify change position + for i := 0 to FIdx.Count-1 do + if FIdx[i] = AFrom then + begin + FIdx[i] := ATo; + Exit; + end; +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.InsertIntoBack(AItem: Pointer); +//var +// LItem: TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.PItem; absolute AItem; !!! bug #25917 +var + LItem: TQueueDictionary.PValue absolute AItem; + LIndex: SizeInt; +begin + LIndex := DoAdd(LItem.Pair.Key, LItem^); + FIdx.Insert(0, LIndex); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.InsertIntoHead(AItem: Pointer); +//var +// LItem: TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.PItem absolute AItem; !!! bug #25917 +var + LItem: TQueueDictionary.PValue absolute AItem; + LIndex: SizeInt; +begin + LIndex := DoAdd(LItem.Pair.Key, LItem^); + FIdx.Add(LIndex); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.IsEmpty: Boolean; +begin + Result := FIdx.Count = 0; +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.Pop: Pointer; +var + AIndex, LGap: SizeInt; + //LResult: TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TItem; !!!bug #25917 +begin + AIndex := FIdx.DoRemove(FIdx.Count - 1, cnExtracted); + + Result := New(TQueueDictionary.PValue); + TQueueDictionary.PValue(Result)^ := DoRemove(AIndex, cnExtracted); +end; + +constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.Create(ACapacity: SizeInt; + const AComparer: IEqualityComparer<TKey>); +begin + FIdx := TList<UInt32>.Create; + inherited Create(ACapacity, AComparer); +end; + +destructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TQueueDictionary.Destroy; +begin + FIdx.Free; +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetQueueCount: SizeInt; +begin + Result := FQueue.Count; +end; + +constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACapacity: SizeInt; + const AComparer: IEqualityComparer<TKey>); +begin +end; + +constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(const AComparer: IEqualityComparer<TKey>); +begin +end; + +constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>; + const AComparer: IEqualityComparer<TKey>); +begin +end; + +constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create; +begin + Create(0); +end; + +constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACapacity: SizeInt); +begin + Create(ACapacity, TExtendedEqualityComparer<TKey>.Default(THashFactory)); +end; + +constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>); +begin + Create(ACollection, TExtendedEqualityComparer<TKey>.Default(THashFactory)); +end; + +constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACapacity: SizeInt; + const AComparer: IExtendedEqualityComparer<TKey>); +begin + FMaxLoadFactor := TCuckooCfg.MAX_LOAD_FACTOR; + FQueue := TQueueDictionary.Create; + FCDM := TCDM.Create; + + // to do - check constraint consts + + if TCuckooCfg.D > THashFactory.MAX_HASHLIST_COUNT then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + // should be moved to class constructor, but bug #24848 + CUCKOO_SIGN := UInt32.GetSizedSignMask(THashFactory.HASH_FUNCTIONS_MASK_SIZE + 1); + CUCKOO_INDEX_SIZE := UInt32.GetBitsLength - (THashFactory.HASH_FUNCTIONS_MASK_SIZE + 1); + CUCKOO_HASH_SIGN := THashFactory.HASH_FUNCTIONS_MASK shl CUCKOO_INDEX_SIZE; + + FEqualityComparer := AComparer; + SetCapacity(ACapacity); +end; + +constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(const AComparer: IExtendedEqualityComparer<TKey>); +begin + Create(0, AComparer); +end; + +constructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create(ACollection: TEnumerable<TDictionaryPair>; + const AComparer: IExtendedEqualityComparer<TKey>); +var + LItem: TPair<TKey, TValue>; +begin + Create(AComparer); + for LItem in ACollection do + Add(LItem); +end; + +destructor TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Destroy; +begin + inherited; + FQueue.Free; + FCDM.Free; +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetKeys: TKeyCollection; +begin + if not Assigned(FKeys) then + FKeys := TKeyCollection.Create(Self); + Result := TKeyCollection(FKeys); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetValues: TValueCollection; +begin + if not Assigned(FValues) then + FValues := TValueCollection.Create(Self); + Result := TValueCollection(FValues); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Lookup(constref AKey: TKey; + var AHashListOrIndex: PUInt32): SizeInt; +begin + Result := Lookup(FItems, AKey, AHashListOrIndex); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Lookup(constref AItems: TItemsDArray; constref AKey: TKey; + var AHashListOrIndex: PUInt32): SizeInt; +var + LLengthMask: SizeInt; + i, j, k: SizeInt; + AHashList: PUInt32 absolute AHashListOrIndex; + AHashListParams: PUInt16 absolute AHashListOrIndex; + AIndex: PtrInt absolute AHashListOrIndex; + // LBloomFilter: UInt32; // to rethink. now is useless +begin + if Length(AItems[0]) = 0 then + Exit(LR_NIL); + + LLengthMask := Length(AItems[0]) - 1; + AHashListParams[0] := TCuckooCfg.D; // number of hashes + + i := 1; // ineks iteracji iteracji haszy + k := 1; // indeks iteracji haszy + // LBloomFilter := 0; + repeat + AHashListParams[1] := i; // iteration + IExtendedEqualityComparer<TKey>(FEqualityComparer).GetHashList(AKey, AHashList); + for j := 0 to THashFactory.HASHLIST_COUNT_PER_FUNCTION[i] - 1 do + begin + AHashList[k] := AHashList[k] or CUCKOO_SIGN; + // LBloomFilter := LBloomFilter or AHashList[k]; + + with AItems[k-1][AHashList[k] and LLengthMask] do + if (Hash and UInt32.GetSignMask) <> 0 then + if (AHashList[k] = Hash or CUCKOO_SIGN) and FEqualityComparer.Equals(AKey, Pair.Key) then + Exit(k-1); + + Inc(k); + end; + Inc(i); + until k > TCuckooCfg.D; + + i := FQueue.FindBucketIndex(AKey); + if i >= 0 then + begin + AIndex := i; + Exit(LR_QUEUE); + end; + +{ LBloomFilter := not LBloomFilter; + for i := 0 to FDicQueueList.Count - 1 do + // with FQueue[i] do + if LBloomFilter and FQueue[i].Hash = 0 then + for j := 1 to TCuckooCfg.D do + if (FQueue[i].Hash or CUCKOO_SIGN = AHashList[j]) then + if FEqualityComparer.Equals(AKey, FQueue[i].Pair.Key) then + begin + AIndex := i; + Exit(LR_QUEUE); + end; } + + Result := LR_NIL; +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.PrepareAddingItem: SizeInt; +var + i: SizeInt; +begin + if FItemsLength > FItemsThreshold then + Rehash(Length(FItems[0]) shl 1) + else if FItemsThreshold = 0 then + begin + for i := 0 to TCuckooCfg.D - 1 do + SetLength(FItems[i], 4); + UpdateItemsThreshold(4); + end + else if FItemsLength = $40000001 then // High(TIndex) ... Error: Type mismatch + OutOfMemoryError; + + Result := FItemsLength; + Inc(FItemsLength); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.UpdateItemsThreshold(ASize: SizeInt); +var + LLength: SizeInt; +begin + LLength := ASize*TCuckooCfg.D; + if LLength = $40000000 then + FItemsThreshold := $40000001 + else + FItemsThreshold := Pred(Round(LLength * FMaxLoadFactor)); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.AddItem(constref AItems: TItemsDArray; constref AKey: TKey; + constref AValue: TValue; const AHashList: PUInt32); +var + LNewItem: TItem; + LPNewItem: PItem; + y: boolean = false; + b: UInt32; + LIndex: UInt32; + i, j, LLengthMask: SizeInt; + LTempItem: TItem; + LHashList: array[0..1] of UInt32; + LHashListParams: array[0..3] of UInt16 absolute LHashList; +begin + LLengthMask := Length(AItems[0]) - 1; + + LNewItem.Pair.Key := AKey; + LNewItem.Pair.Value := AValue; + // by concept already sign bit is set + LNewItem.Hash := ((not CUCKOO_HASH_SIGN) and AHashList[1]) or UInt32.GetSignMask; // start at array [0] + FQueue.InsertIntoBack(@LNewItem); + + for i := 0 to TCuckooCfg.L - 1 do + begin + if not y then + if FQueue.IsEmpty then + Exit + else + begin + LPNewItem := FQueue.Pop; // bug #25917 workaround + LNewItem := LPNewItem^; + Dispose(LPNewItem); + b := (LNewItem.Hash and CUCKOO_HASH_SIGN) shr CUCKOO_INDEX_SIZE; + y := true; + end; + LIndex := LNewItem.Hash and LLengthMask; + if (AItems[b][LIndex].Hash and UInt32.GetSignMask) = 0 then // insert! + begin + AItems[b][LIndex] := LNewItem; + FCDM.Clear; + y := false; + end + else + begin + if FCDM.ContainsKey(LNewItem.Pair.Key) then // found second cycle + begin + FQueue.InsertIntoBack(@LNewItem); + FCDM.Clear; + y := false; + end + else + begin + LTempItem := AItems[b][LIndex]; + AItems[b][LIndex] := LNewItem; + LNewItem.Hash := LNewItem.Hash or CUCKOO_SIGN; + FCDM.AddOrSetValue(LNewItem.Pair.Key, EmptyRecord); + + LNewItem := LTempItem; + b := b + 1; + if b >= TCuckooCfg.D then + b := 0; + LHashListParams[0] := -Succ(b); + IExtendedEqualityComparer<TKey>(FEqualityComparer).GetHashList(LNewItem.Pair.Key, @LHashList[0]); + LNewItem.Hash := (LHashList[1] and not CUCKOO_SIGN) or (b shl CUCKOO_INDEX_SIZE) or UInt32.GetSignMask; + // y := True; // always true in this place + end; + end; + end; + if y then + FQueue.InsertIntoHead(@LNewItem); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.DoAdd(constref AKey: TKey; constref AValue: TValue; + const AHashList: PUInt32); +begin + AddItem(FItems, AKey, AValue, AHashList); + KeyNotify(AKey, cnAdded); + ValueNotify(AValue, cnAdded); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Add(constref AKey: TKey; constref AValue: TValue); +var + LHashList: array[0..TCuckooCfg.D] of UInt32; + LHashListOrIndex: PUint32; +begin + PrepareAddingItem; + LHashListOrIndex := @LHashList[0]; + if Lookup(AKey, LHashListOrIndex) <> LR_NIL then + raise EListError.CreateRes(@SDuplicatesNotAllowed); + + DoAdd(AKey, AValue, LHashListOrIndex); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Add(constref APair: TPair<TKey, TValue>); +begin + Add(APair.Key, APair.Value); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.DoRemove(const AHashListOrIndex: PUInt32; + ALookupResult: SizeInt; ACollectionNotification: TCollectionNotification): TValue; +var + LItem: PItem; + LIndex: UInt32; + LQueueIndex: SizeInt absolute AHashListOrIndex; + LPair: TPair<TKey, TValue>; +begin + case ALookupResult of + LR_QUEUE: + LPair := FQueue.FItems[LQueueIndex].Pair.Value.Pair; + LR_NIL: + raise ERangeError.Create(SItemNotFound); + else + LIndex := AHashListOrIndex[ALookupResult + 1] and (Length(FItems[0]) - 1); + LItem := @FItems[ALookupResult][LIndex]; + LItem.Hash := 0; + LPair := LItem.Pair; + LItem.Pair := Default(TPair<TKey, TValue>); + end; + + Result := LPair.Value; + Dec(FItemsLength); + if ALookupResult = LR_QUEUE then + begin + FQueue.FIdx.Remove(LQueueIndex); + FQueue.DoRemove(LQueueIndex, cnRemoved); + end; + + FCDM.Remove(LPair.Key); // item can exist in CDM + + PairNotify(LPair, ACollectionNotification); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Remove(constref AKey: TKey); +var + LHashList: array[0..TCuckooCfg.D] of UInt32; + LHashListOrIndex: PUint32; + LLookupResult: SizeInt; +begin + LHashListOrIndex := @LHashList[0]; + LLookupResult := Lookup(AKey, LHashListOrIndex); + if LLookupResult = LR_NIL then + Exit; + + DoRemove(LHashListOrIndex, LLookupResult, cnRemoved); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ExtractPair(constref AKey: TKey): TPair<TKey, TValue>; +var + LHashList: array[0..TCuckooCfg.D] of UInt32; + LHashListOrIndex: PUint32; + LLookupResult: SizeInt; +begin + LHashListOrIndex := @LHashList[0]; + LLookupResult := Lookup(AKey, LHashListOrIndex); + if LLookupResult = LR_NIL then + Exit(Default(TPair<TKey, TValue>)); + + Result.Key := AKey; + Result.Value := DoRemove(LHashListOrIndex, LLookupResult, cnExtracted); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Clear; +var + LItem: PItem; + i, j: SizeInt; + LOldItems: TItemsDArray; + LOldQueueItems: TQueueDictionary.TItemsArray; + LQueueItem: TQueueDictionary._TItem; +begin + FItemsLength := 0; + FItemsThreshold := 0; + LOldItems := FItems; + for i := 0 to TCuckooCfg.D - 1 do + FItems[i] := nil; + + for i := 0 to TCuckooCfg.D - 1 do + begin + for j := 0 to High(LOldItems[0]) do + begin + LItem := @LOldItems[i][j]; + if (LItem.Hash and UInt32.GetSignMask <> 0) then + PairNotify(LItem.Pair, cnRemoved); + end; + end; + + FCDM.Clear; + + // queue + FQueue.FItemsLength := 0; + FQueue.FItemsThreshold := 0; + LOldQueueItems := FQueue.FItems; + FQueue.FItems := nil; + + for i := 0 to High(LOldQueueItems) do + begin + LQueueItem := TQueueDictionary._TItem(LOldQueueItems[i]); + if (LQueueItem.Hash and UInt32.GetSignMask = 0) then + Continue; + + PairNotify(LQueueItem.Pair.Value.Pair, cnRemoved); + end; +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Rehash(ASizePow2: SizeInt); +var + LNewItems: TItemsDArray; + LHash: UInt32; + LIndex: SizeInt; + i, j: SizeInt; + LItem, LNewItem: PItem; + LOldQueue: TQueueDictionary; +var + LHashList: array[0..1] of UInt32; + LHashListParams: array[0..3] of Int16 absolute LHashList; +begin + if ASizePow2 = Length(FItems[0]) then + Exit; + if ASizePow2 < 0 then + OutOfMemoryError; + + for i := 0 to TCuckooCfg.D - 1 do + SetLength(LNewItems[i], ASizePow2); + + LHashListParams[0] := -1; + + // opportunity to clear the queue + LOldQueue := FQueue; + FCDM.Clear; + FQueue := TQueueDictionary.Create; + for i := 0 to LOldQueue.FIdx.Count - 1 do + begin + LItem := @LOldQueue.FItems[LOldQueue.FIdx[i]].Pair.Value; + LHashList[1] := FEqualityComparer.GetHashCode(LItem.Pair.Key); + AddItem(LNewItems, LItem.Pair.Key, LItem.Pair.Value, @LHashList[0]); + end; + LOldQueue.Free; + + // copy the old elements + for i := 0 to TCuckooCfg.D - 1 do + for j := 0 to High(FItems[0]) do + begin + LItem := @FItems[i][j]; + if (LItem.Hash and UInt32.GetSignMask) = 0 then + Continue; + + // small optimization. most of items exist in table 0 + if LItem.Hash and CUCKOO_HASH_SIGN = 0 then + begin + LHashList[1] := LItem.Hash; + AddItem(LNewItems, LItem.Pair.Key, LItem.Pair.Value, @LHashList[0]); + end + else + begin + LHashList[1] := FEqualityComparer.GetHashCode(LItem.Pair.Key); + AddItem(LNewItems, LItem.Pair.Key, LItem.Pair.Value, @LHashList[0]); + end; + end; + + FItems := LNewItems; + UpdateItemsThreshold(ASizePow2); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.DoGetEnumerator: TEnumerator<TDictionaryPair>; +begin + Result := GetEnumerator; +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.SetCapacity(ACapacity: SizeInt); +begin + if ACapacity < FItemsLength then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + Resize(ACapacity); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.SetMaxLoadFactor(AValue: single); +var + LItemsLength: SizeInt; +begin + if (AValue > TCuckooCfg.MAX_LOAD_FACTOR) or (AValue <= 0) then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + FMaxLoadFactor := AValue; + + repeat + LItemsLength := Length(FItems[0]); + UpdateItemsThreshold(LItemsLength); + if FItemsLength > FItemsThreshold then + Rehash(LItemsLength shl 1); + until FItemsLength <= FItemsThreshold; +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetLoadFactor: single; +begin + Result := FItemsLength / (Length(FItems[0]) * TCuckooCfg.D); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetCapacity: SizeInt; +begin + Result := Length(FItems[0]) * TCuckooCfg.D; +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Resize(ANewSize: SizeInt); +var + LNewSize: SizeInt; +begin + if ANewSize < 0 then + raise EArgumentOutOfRangeException.CreateRes(@SArgumentOutOfRange); + + LNewSize := 0; + if ANewSize > 0 then + begin + LNewSize := 4; + while LNewSize * TCuckooCfg.D < ANewSize do + LNewSize := LNewSize shl 1; + end; + + Rehash(LNewSize); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetEnumerator: TPairEnumerator; +begin + Result := TPairEnumerator.Create(Self); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetItem(const AKey: TKey): TValue; +var + LHashList: array[0..TCuckooCfg.D] of UInt32; + LHashListOrIndex: PUint32; + LLookupResult: SizeInt; + LIndex: UInt32; +begin + LHashListOrIndex := @LHashList[0]; + LLookupResult := Lookup(AKey, LHashListOrIndex); + + case LLookupResult of + LR_QUEUE: + Result := FQueue.FItems[PtrInt(LHashListOrIndex)].Pair.Value.Pair.Value; + LR_NIL: + raise EListError.CreateRes(@SDictionaryKeyDoesNotExist); + else + LIndex := LHashListOrIndex[LLookupResult + 1] and (Length(FItems[0]) - 1); + Result := FItems[LLookupResult][LIndex].Pair.Value; + end; +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TrimExcess; +begin + SetCapacity(Succ(FItemsLength)); +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.SetItem(constref AValue: TValue; + const AHashListOrIndex: PUInt32; ALookupResult: SizeInt); +var + LIndex: UInt32; +begin + case ALookupResult of + LR_QUEUE: + SetValue(FQueue.FItems[PtrInt(AHashListOrIndex)].Pair.Value.Pair.Value, AValue); + LR_NIL: + raise EListError.CreateRes(@SItemNotFound); + else + LIndex := AHashListOrIndex[ALookupResult + 1] and (Length(FItems[0]) - 1); + SetValue(FItems[ALookupResult][LIndex].Pair.Value, AValue); + end; +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.SetItem(const AKey: TKey; const AValue: TValue); +var + LHashList: array[0..TCuckooCfg.D] of UInt32; + LHashListOrIndex: PUint32; + LLookupResult: SizeInt; + LIndex: UInt32; +begin + LHashListOrIndex := @LHashList[0]; + LLookupResult := Lookup(AKey, LHashListOrIndex); + + SetItem(AValue, LHashListOrIndex, LLookupResult); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TryGetValue(constref AKey: TKey; out AValue: TValue): Boolean; +var + LHashList: array[0..TCuckooCfg.D] of UInt32; + LHashListOrIndex: PUint32; + LLookupResult: SizeInt; + LIndex: UInt32; +begin + LHashListOrIndex := @LHashList[0]; + LLookupResult := Lookup(AKey, LHashListOrIndex); + + Result := LLookupResult <> LR_NIL; + + case LLookupResult of + LR_QUEUE: + AValue := FQueue.FItems[PtrInt(LHashListOrIndex)].Pair.Value.Pair.Value; + LR_NIL: + AValue := Default(TValue); + else + LIndex := LHashListOrIndex[LLookupResult + 1] and (Length(FItems[0]) - 1); + AValue := FItems[LLookupResult][LIndex].Pair.Value; + end; +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.AddOrSetValue(constref AKey: TKey; constref AValue: TValue); +var + LHashList: array[0..TCuckooCfg.D] of UInt32; + LHashListOrIndex: PUint32; + LLookupResult: SizeInt; + LIndex: UInt32; +begin + LHashListOrIndex := @LHashList[0]; + LLookupResult := Lookup(AKey, LHashListOrIndex); + + if LLookupResult = LR_NIL then + begin + PrepareAddingItem; + DoAdd(AKey, AValue, LHashListOrIndex); + end + else + SetItem(AValue, LHashListOrIndex, LLookupResult); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ContainsKey(constref AKey: TKey): Boolean; +var + LHashList: array[0..TCuckooCfg.D] of UInt32; + LHashListOrIndex: PUint32; +begin + LHashListOrIndex := @LHashList[0]; + Result := Lookup(AKey, LHashListOrIndex) <> LR_NIL; +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ContainsValue(constref AValue: TValue): Boolean; +begin + Result := ContainsValue(AValue, TEqualityComparer<TValue>.Default(THashFactory)); +end; + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ContainsValue(constref AValue: TValue; + const AEqualityComparer: IEqualityComparer<TValue>): Boolean; +var + i, j: SizeInt; + LItem: PItem; +begin + if Length(FItems[0]) = 0 then + Exit(False); + + for i := 0 to TCuckooCfg.D - 1 do + for j := 0 to High(FItems[0]) do + begin + LItem := @FItems[i][j]; + if (LItem.Hash and UInt32.GetSignMask) = 0 then + Continue; + + if AEqualityComparer.Equals(AValue, LItem.Pair.Value) then + Exit(True); + end; + Result := False; +end; + +procedure TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.GetMemoryLayout( + const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition); +var + i, j, k: SizeInt; +begin + k := 0; + for i := 0 to TCuckooCfg.D - 1 do + for j := 0 to High(FItems[0]) do + begin + if FItems[i][j].Hash and UInt32.GetSignMask <> 0 then + AOnGetMemoryLayoutKeyPosition(Self, k); + inc(k); + end; +end; + +{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPairEnumerator } + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TPairEnumerator.GetCurrent: TPair<TKey, TValue>; +begin + if FMainIndex = TCuckooCfg.D then + Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair + else + Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair; +end; + +{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TValueEnumerator } + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TValueEnumerator.GetCurrent: TValue; +begin + if FMainIndex = TCuckooCfg.D then + Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair.Value + else + Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair.Value; +end; + +{ TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TKeyEnumerator } + +function TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.TKeyEnumerator.GetCurrent: TKey; +begin + if FMainIndex = TCuckooCfg.D then + Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FQueue.FItems[FIndex].Pair.Value.Pair.Key + else + Result := TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>(FDictionary).FItems[FMainIndex][FIndex].Pair.Key; +end; + +{ TObjectDictionary<DICTIONARY_CONSTRAINTS> } + +procedure TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.KeyNotify( + constref AKey: TKey; ACollectionNotification: TCollectionNotification); +begin + inherited; + + if (doOwnsKeys in FOwnerships) and (ACollectionNotification = cnRemoved) then + TObject(AKey).Free; +end; + +procedure TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.ValueNotify(constref AValue: TValue; + ACollectionNotification: TCollectionNotification); +begin + inherited; + + if (doOwnsValues in FOwnerships) and (ACollectionNotification = cnRemoved) then + TObject(AValue).Free; +end; + +constructor TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create( + AOwnerships: TDictionaryOwnerships); +begin + Create(AOwnerships, 0); +end; + +constructor TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create( + AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt); +begin + inherited Create(ACapacity); + + FOwnerships := AOwnerships; +end; + +constructor TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create( + AOwnerships: TDictionaryOwnerships; const AComparer: IExtendedEqualityComparer<TKey>); +begin + inherited Create(AComparer); + + FOwnerships := AOwnerships; +end; + +constructor TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>.Create( + AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer<TKey>); +begin + inherited Create(ACapacity, AComparer); + + FOwnerships := AOwnerships; +end; + +procedure TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.KeyNotify( + constref AKey: TKey; ACollectionNotification: TCollectionNotification); +begin + inherited; + + if (doOwnsKeys in FOwnerships) and (ACollectionNotification = cnRemoved) then + TObject(AKey).Free; +end; + +procedure TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.ValueNotify( + constref AValue: TValue; ACollectionNotification: TCollectionNotification); +begin + inherited; + + if (doOwnsValues in FOwnerships) and (ACollectionNotification = cnRemoved) then + TObject(AValue).Free; +end; + +constructor TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.Create(AOwnerships: TDictionaryOwnerships); +begin + Create(AOwnerships, 0); +end; + +constructor TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.Create(AOwnerships: TDictionaryOwnerships; + ACapacity: SizeInt); +begin + inherited Create(ACapacity); + + FOwnerships := AOwnerships; +end; + +constructor TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.Create(AOwnerships: TDictionaryOwnerships; + const AComparer: IEqualityComparer<TKey>); +begin + inherited Create(AComparer); + + FOwnerships := AOwnerships; +end; + +constructor TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>.Create(AOwnerships: TDictionaryOwnerships; + ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); +begin + inherited Create(ACapacity, AComparer); + + FOwnerships := AOwnerships; +end; diff --git a/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/inc/generics.dictionariesh.inc b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/inc/generics.dictionariesh.inc new file mode 100644 index 000000000..adfc91415 --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/Generics.Collection/src/inc/generics.dictionariesh.inc @@ -0,0 +1,533 @@ +{%MainUnit generics.collections.pas} + +{ + This file is part of the Free Pascal run time library. + Copyright (c) 2014 by Maciej Izak (hnb) + member of the Free Sparta development team (http://freesparta.com) + + Copyright(c) 2004-2014 DaThoX + + It contains the Free Pascal generics library + + See the file COPYING.FPC, included in this distribution, + for details about the copyright. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + **********************************************************************} + +{$WARNINGS OFF} +type + TEmptyRecord = record // special record for Dictionary TValue (Dictionary as Set) + end; + + { TPair } + + TPair<TKey, TValue> = record + public + Key: TKey; + Value: TValue; + class function Create(AKey: TKey; AValue: TValue): TPair<TKey, TValue>; static; + end; + + { TCustomDictionary } + + // bug #24283 and #24097 (forward declaration) - should be: + // TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS> = class(TEnumerable<TPair<TKey, TValue> >); + TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract + public type + // workaround... no generics types in generics types + TDictionaryPair = TPair<TKey, TValue>; + PDictionaryPair = ^TDictionaryPair; + PKey = ^TKey; + PValue = ^TValue; + THashFactoryClass = THashFactory; + public + FItemsLength: SizeInt; + FEqualityComparer: IEqualityComparer<TKey>; + FKeys: TEnumerable<TKey>; + FValues: TEnumerable<TValue>; + FMaxLoadFactor: single; + protected + procedure SetCapacity(ACapacity: SizeInt); virtual; abstract; + // bug #24283. workaround for this class because can't inherit from TEnumerable + function DoGetEnumerator: TEnumerator<TDictionaryPair>; virtual; abstract; {override;} + + procedure SetMaxLoadFactor(AValue: single); virtual; abstract; + function GetLoadFactor: single; virtual; abstract; + function GetCapacity: SizeInt; virtual; abstract; + public + property MaxLoadFactor: single read FMaxLoadFactor write SetMaxLoadFactor; + property LoadFactor: single read GetLoadFactor; + property Capacity: SizeInt read GetCapacity write SetCapacity; + + property Count: SizeInt read FItemsLength; + + procedure Clear; virtual; abstract; + procedure Add(constref APair: TPair<TKey, TValue>); virtual; abstract; + strict private // bug #24283. workaround for this class because can't inherit from TEnumerable + function ToArray(ACount: SizeInt): TArray<TDictionaryPair>; overload; + public + function ToArray: TArray<TDictionaryPair>; virtual; final; {override; final; // bug #24283} overload; + + constructor Create; virtual; overload; + constructor Create(ACapacity: SizeInt); virtual; overload; + constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); virtual; overload; + constructor Create(const AComparer: IEqualityComparer<TKey>); overload; + constructor Create(ACollection: TEnumerable<TDictionaryPair>); virtual; overload; + constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); virtual; overload; + + destructor Destroy; override; + private + FOnKeyNotify: TCollectionNotifyEvent<TKey>; + FOnValueNotify: TCollectionNotifyEvent<TValue>; + protected + procedure UpdateItemsThreshold(ASize: SizeInt); virtual; abstract; + + procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); virtual; + procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); virtual; + procedure PairNotify(constref APair: TPair<TKey, TValue>; ACollectionNotification: TCollectionNotification); inline; + procedure SetValue(var AValue: TValue; constref ANewValue: TValue); + public + property OnKeyNotify: TCollectionNotifyEvent<TKey> read FOnKeyNotify write FOnKeyNotify; + property OnValueNotify: TCollectionNotifyEvent<TValue> read FOnValueNotify write FOnValueNotify; + end; + + { TCustomDictionaryEnumerator } + + TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract(TEnumerator< T >) + private + FDictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>; + FIndex: SizeInt; + protected + function DoGetCurrent: T; override; + function GetCurrent: T; virtual; abstract; + public + constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>); + end; + + { TDictionaryEnumerable } + + TDictionaryEnumerable<TDictionaryEnumerator: TObject; // ... inherits from TCustomDictionaryEnumerator. workaround... + T, CUSTOM_DICTIONARY_CONSTRAINTS> = class abstract(TEnumerable<T>) + private + FDictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>; + function GetCount: SizeInt; + public + constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>); + function DoGetEnumerator: TDictionaryEnumerator; override; + function ToArray: TArray<T>; override; final; + property Count: SizeInt read GetCount; + end; + + // more info : http://en.wikipedia.org/wiki/Open_addressing + + { TDictionaryEnumerable } + + TOpenAddressingEnumerator<T, OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>) + protected + function DoMoveNext: Boolean; override; + end; + + TOnGetMemoryLayoutKeyPosition = procedure(Sender: TObject; AKeyPos: UInt32) of object; + + TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>) + private type + PItem = ^TItem; + TItem = record + Hash: UInt32; + Pair: TPair<TKey, TValue>; + end; + + TItemsArray = array of TItem; + private var + FItemsThreshold: SizeInt; + FItems: TItemsArray; + + procedure Resize(ANewSize: SizeInt); + function PrepareAddingItem: SizeInt; + protected + function RealItemsLength: SizeInt; virtual; + function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): boolean; virtual; + function FindBucketIndex(constref AKey: TKey): SizeInt; overload; inline; + function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey; out AHash: UInt32): SizeInt; virtual; abstract; overload; + public + type + // Enumerators + TPairEnumerator = class(TOpenAddressingEnumerator<TDictionaryPair, OPEN_ADDRESSING_CONSTRAINTS>) + protected + function GetCurrent: TPair<TKey,TValue>; override; + end; + + TValueEnumerator = class(TOpenAddressingEnumerator<TValue, OPEN_ADDRESSING_CONSTRAINTS>) + protected + function GetCurrent: TValue; override; + end; + + TKeyEnumerator = class(TOpenAddressingEnumerator<TKey, OPEN_ADDRESSING_CONSTRAINTS>) + protected + function GetCurrent: TKey; override; + end; + + // Collections + TValueCollection = class(TDictionaryEnumerable<TValueEnumerator, TValue, CUSTOM_DICTIONARY_CONSTRAINTS>); + + TKeyCollection = class(TDictionaryEnumerable<TKeyEnumerator, TKey, CUSTOM_DICTIONARY_CONSTRAINTS>); + + // bug #24283 - workaround related to lack of DoGetEnumerator + function GetEnumerator: TPairEnumerator; reintroduce; + private + function GetKeys: TKeyCollection; + function GetValues: TValueCollection; + private + function GetItem(const AKey: TKey): TValue; inline; + procedure SetItem(const AKey: TKey; const AValue: TValue); inline; + procedure AddItem(var AItem: TItem; constref AKey: TKey; constref AValue: TValue; const AHash: UInt32); inline; + protected + // useful for using dictionary as array + function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): TValue; virtual; + function DoAdd(constref AKey: TKey; constref AValue: TValue): SizeInt; virtual; + + procedure UpdateItemsThreshold(ASize: SizeInt); override; + + procedure SetCapacity(ACapacity: SizeInt); override; + // bug #24283 - can't descadent from TEnumerable + function DoGetEnumerator: TEnumerator<TDictionaryPair>; override; + procedure SetMaxLoadFactor(AValue: single); override; + function GetLoadFactor: single; override; + function GetCapacity: SizeInt; override; + public + // many constructors because bug #25607 + constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload; + + procedure Add(constref APair: TPair<TKey, TValue>); override; overload; + procedure Add(constref AKey: TKey; constref AValue: TValue); overload; inline; + procedure Remove(constref AKey: TKey); + function ExtractPair(constref AKey: TKey): TPair<TKey, TValue>; + procedure Clear; override; + procedure TrimExcess; + function TryGetValue(constref AKey: TKey; out AValue: TValue): Boolean; + procedure AddOrSetValue(constref AKey: TKey; constref AValue: TValue); + function ContainsKey(constref AKey: TKey): Boolean; inline; + function ContainsValue(constref AValue: TValue): Boolean; overload; + function ContainsValue(constref AValue: TValue; const AEqualityComparer: IEqualityComparer<TValue>): Boolean; virtual; overload; + + property Items[Index: TKey]: TValue read GetItem write SetItem; default; + property Keys: TKeyCollection read GetKeys; + property Values: TValueCollection read GetValues; + + procedure GetMemoryLayout(const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition); + end; + + TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>) + private type // for workaround Lazarus bug #25613 + _TItem = record + Hash: UInt32; + Pair: TPair<TKey, TValue>; + end; + protected + procedure NotifyIndexChange(AFrom, ATo: SizeInt); virtual; + function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): TValue; override; + function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey; out AHash: UInt32): SizeInt; override; overload; + end; + + // More info and TODO + // https://github.com/OpenHFT/UntitledCollectionsProject/wiki/Tombstones-purge-from-hashtable:-theory-and-practice + + TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS> = class abstract(TOpenAddressing<OPEN_ADDRESSING_CONSTRAINTS>) + private + FTombstonesCount: SizeInt; + protected + function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): boolean; override; + function RealItemsLength: SizeInt; override; + + function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; constref AKey: TKey; + out AHash: UInt32): SizeInt; virtual; abstract; + + function DoRemove(AIndex: SizeInt; ACollectionNotification: TCollectionNotification): TValue; override; + function DoAdd(constref AKey: TKey; constref AValue: TValue): SizeInt; override; + public + property TombstonesCount: SizeInt read FTombstonesCount; + procedure ClearTombstones; virtual; + procedure Clear; override; + end; + + TOpenAddressingSH<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>) + private type // for workaround Lazarus bug #25613 + _TItem = record + Hash: UInt32; + Pair: TPair<TKey, TValue>; + end; + protected + function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey; + out AHash: UInt32): SizeInt; override; overload; + function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; constref AKey: TKey; + out AHash: UInt32): SizeInt; override; + end; + + TOpenAddressingDH<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingTombstones<OPEN_ADDRESSING_CONSTRAINTS>) + private type // for workaround Lazarus bug #25613 + _TItem = record + Hash: UInt32; + Pair: TPair<TKey, TValue>; + end; + private + R: UInt32; + protected + procedure UpdateItemsThreshold(ASize: SizeInt); override; + function FindBucketIndex(constref AItems: TArray<TItem>; constref AKey: TKey; + out AHash: UInt32): SizeInt; override; overload; + function FindBucketIndexOrTombstone(constref AItems: TArray<TItem>; constref AKey: TKey; + out AHash: UInt32): SizeInt; override; + strict protected + constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload; + constructor Create(const AComparer: IEqualityComparer<TKey>); reintroduce; overload; + constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload; + public // bug #26181 (redundancy of constructors) + constructor Create(ACapacity: SizeInt); override; overload; + constructor Create(ACollection: TEnumerable<TDictionaryPair>); override; overload; + constructor Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload; + constructor Create(const AComparer: IExtendedEqualityComparer<TKey>); overload; + constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload; + end; + + TDeamortizedDArrayCuckooMapEnumerator<T, CUCKOO_CONSTRAINTS> = class abstract(TCustomDictionaryEnumerator<T, CUSTOM_DICTIONARY_CONSTRAINTS>) + private type // for workaround Lazarus bug #25613 + TItem = record + Hash: UInt32; + Pair: TPair<TKey, TValue>; + end; + TItemsArray = array of TItem; + private + FMainIndex: SizeInt; + protected + function DoMoveNext: Boolean; override; + public + constructor Create(ADictionary: TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>); + end; + + // more info : + // http://arxiv.org/abs/0903.0391 + + TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS> = class(TCustomDictionary<CUSTOM_DICTIONARY_CONSTRAINTS>) + private const // Lookup Result + LR_NIL = -1; + LR_QUEUE = -2; + private type + PItem = ^TItem; + TItem = record + Hash: UInt32; + Pair: TPair<TKey, TValue>; + end; + TValueForQueue = TItem; + + TQueueDictionary = class(TOpenAddressingLP<TKey, TValueForQueue, TDelphiHashFactory, TLinearProbing>) + private type // for workaround Lazarus bug #25613 + _TItem = record + Hash: UInt32; + Pair: TPair<TKey, TValueForQueue>; + end; + private + FIdx: TList<UInt32>; // list to keep order + protected + procedure NotifyIndexChange(AFrom, ATo: SizeInt); override; + function Rehash(ASizePow2: SizeInt; AForce: Boolean = False): Boolean; override; + public + procedure InsertIntoBack(AItem: Pointer); + procedure InsertIntoHead(AItem: Pointer); + function IsEmpty: Boolean; + function Pop: Pointer; + constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload; + destructor Destroy; override; + end; + + // cycle-detection mechanism class + TCDM = class(TOpenAddressingSH<TKey, TEmptyRecord, TDelphiHashFactory, TLinearProbing>); + TItemsArray = array of TItem; + TItemsDArray = array[0..Pred(TCuckooCfg.D)] of TItemsArray; + private var + FQueue: TQueueDictionary; // probably can be optimized - hash TItem give information from TItem.Hash for cuckoo ... + // currently is kept in "TQueueDictionary = class(TOpenAddressingSH<TKey, TItem, ...>" + + FCDM: TCDM; // cycle-detection mechanism + FItemsThreshold: SizeInt; + FItems: TItemsDArray; + // sadly there is bug #24848 for class var ... + {class} var + CUCKOO_SIGN, CUCKOO_INDEX_SIZE, CUCKOO_HASH_SIGN: UInt32; + // CUCKOO_MAX_ITEMS_LENGTH: <- to do : calc max length for items based on CUCKOO sign + // maybe some CDM bloom filter? + + procedure UpdateItemsThreshold(ASize: SizeInt); override; + procedure Resize(ANewSize: SizeInt); + procedure Rehash(ASizePow2: SizeInt); + function PrepareAddingItem: SizeInt; + protected + function Lookup(constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; inline; overload; + function Lookup(constref AItems: TItemsDArray; constref AKey: TKey; var AHashListOrIndex: PUInt32): SizeInt; virtual; overload; + public + type + // Enumerators + TPairEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<TDictionaryPair, CUCKOO_CONSTRAINTS>) + protected + function GetCurrent: TPair<TKey,TValue>; override; + end; + + TValueEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<TValue, CUCKOO_CONSTRAINTS>) + protected + function GetCurrent: TValue; override; + end; + + TKeyEnumerator = class(TDeamortizedDArrayCuckooMapEnumerator<TKey, CUCKOO_CONSTRAINTS>) + protected + function GetCurrent: TKey; override; + end; + + // Collections + TValueCollection = class(TDictionaryEnumerable<TValueEnumerator, TValue, CUSTOM_DICTIONARY_CONSTRAINTS>); + + TKeyCollection = class(TDictionaryEnumerable<TKeyEnumerator, TKey, CUSTOM_DICTIONARY_CONSTRAINTS>); + + // bug #24283 - workaround related to lack of DoGetEnumerator + function GetEnumerator: TPairEnumerator; reintroduce; + private + function GetKeys: TKeyCollection; + function GetValues: TValueCollection; + private + function GetItem(const AKey: TKey): TValue; inline; + procedure SetItem(const AKey: TKey; const AValue: TValue); overload; inline; + procedure SetItem(constref AValue: TValue; const AHashListOrIndex: PUInt32; ALookupResult: SizeInt); overload; + + procedure AddItem(constref AItems: TItemsDArray; constref AKey: TKey; constref AValue: TValue; const AHashList: PUInt32); overload; + procedure DoAdd(constref AKey: TKey; constref AValue: TValue; const AHashList: PUInt32); overload; inline; + function DoRemove(const AHashListOrIndex: PUInt32; ALookupResult: SizeInt; + ACollectionNotification: TCollectionNotification): TValue; + + function GetQueueCount: SizeInt; + protected + procedure SetCapacity(ACapacity: SizeInt); override; + // bug #24283 - can't descadent from TEnumerable + function DoGetEnumerator: TEnumerator<TDictionaryPair>; override; + procedure SetMaxLoadFactor(AValue: single); override; + function GetLoadFactor: single; override; + function GetCapacity: SizeInt; override; + strict protected // bug #26181 + constructor Create(ACapacity: SizeInt; const AComparer: IEqualityComparer<TKey>); override; overload; + constructor Create(const AComparer: IEqualityComparer<TKey>); reintroduce; overload; + constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IEqualityComparer<TKey>); override; overload; + public + // TODO: function TryFlushQueue(ACount: SizeInt): SizeInt; + + constructor Create; override; overload; + constructor Create(ACapacity: SizeInt); override; overload; + constructor Create(ACollection: TEnumerable<TDictionaryPair>); override; overload; + constructor Create(ACapacity: SizeInt; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload; + constructor Create(const AComparer: IExtendedEqualityComparer<TKey>); overload; + constructor Create(ACollection: TEnumerable<TDictionaryPair>; const AComparer: IExtendedEqualityComparer<TKey>); virtual; overload; + destructor Destroy; override; + + procedure Add(constref APair: TPair<TKey, TValue>); override; overload; + procedure Add(constref AKey: TKey; constref AValue: TValue); overload; + procedure Remove(constref AKey: TKey); + function ExtractPair(constref AKey: TKey): TPair<TKey, TValue>; + procedure Clear; override; + procedure TrimExcess; + function TryGetValue(constref AKey: TKey; out AValue: TValue): Boolean; + procedure AddOrSetValue(constref AKey: TKey; constref AValue: TValue); + function ContainsKey(constref AKey: TKey): Boolean; inline; + function ContainsValue(constref AValue: TValue): Boolean; overload; + function ContainsValue(constref AValue: TValue; const AEqualityComparer: IEqualityComparer<TValue>): Boolean; virtual; overload; + + property Items[Index: TKey]: TValue read GetItem write SetItem; default; + property Keys: TKeyCollection read GetKeys; + property Values: TValueCollection read GetValues; + + property QueueCount: SizeInt read GetQueueCount; + procedure GetMemoryLayout(const AOnGetMemoryLayoutKeyPosition: TOnGetMemoryLayoutKeyPosition); + end; + + TDictionaryOwnerships = set of (doOwnsKeys, doOwnsValues); + + TObjectDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS> = class(TDeamortizedDArrayCuckooMap<CUCKOO_CONSTRAINTS>) + private + FOwnerships: TDictionaryOwnerships; + protected + procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); override; + procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); override; + public + // can't be as "Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt = 0)" + // because bug #25607 + constructor Create(AOwnerships: TDictionaryOwnerships); overload; + constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt); overload; + constructor Create(AOwnerships: TDictionaryOwnerships; + const AComparer: IExtendedEqualityComparer<TKey>); overload; + constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt; + const AComparer: IExtendedEqualityComparer<TKey>); overload; + end; + + TObjectOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS> = class(TOpenAddressingLP<OPEN_ADDRESSING_CONSTRAINTS>) + private + FOwnerships: TDictionaryOwnerships; + protected + procedure KeyNotify(constref AKey: TKey; ACollectionNotification: TCollectionNotification); override; + procedure ValueNotify(constref AValue: TValue; ACollectionNotification: TCollectionNotification); override; + public + // can't be as "Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt = 0)" + // because bug #25607 + constructor Create(AOwnerships: TDictionaryOwnerships); overload; + constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt); overload; + constructor Create(AOwnerships: TDictionaryOwnerships; + const AComparer: IEqualityComparer<TKey>); overload; + constructor Create(AOwnerships: TDictionaryOwnerships; ACapacity: SizeInt; + const AComparer: IEqualityComparer<TKey>); overload; + end; + + // useful generics overloads + TOpenAddressingLP<TKey, TValue, THashFactory> = class(TOpenAddressingLP<TKey, TValue, THashFactory, TLinearProbing>); + TOpenAddressingLP<TKey, TValue> = class(TOpenAddressingLP<TKey, TValue, TDelphiHashFactory, TLinearProbing>); + + TObjectOpenAddressingLP<TKey, TValue, THashFactory> = class(TObjectOpenAddressingLP<TKey, TValue, THashFactory, TLinearProbing>); + TObjectOpenAddressingLP<TKey, TValue> = class(TObjectOpenAddressingLP<TKey, TValue, TDelphiHashFactory, TLinearProbing>); + + // Linear Probing with Tombstones (LPT) + TOpenAddressingLPT<TKey, TValue, THashFactory> = class(TOpenAddressingSH<TKey, TValue, THashFactory, TLinearProbing>); + TOpenAddressingLPT<TKey, TValue> = class(TOpenAddressingSH<TKey, TValue, TDelphiHashFactory, TLinearProbing>); + + TOpenAddressingQP<TKey, TValue, THashFactory> = class(TOpenAddressingSH<TKey, TValue, THashFactory, TQuadraticProbing>); + TOpenAddressingQP<TKey, TValue> = class(TOpenAddressingSH<TKey, TValue, TDelphiHashFactory, TQuadraticProbing>); + + TOpenAddressingDH<TKey, TValue, THashFactory> = class(TOpenAddressingDH<TKey, TValue, THashFactory, TDoubleHashing>); + TOpenAddressingDH<TKey, TValue> = class(TOpenAddressingDH<TKey, TValue, TDelphiDoubleHashFactory, TDoubleHashing>); + + TCuckooD2<TKey, TValue, THashFactory> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D2>); + TCuckooD2<TKey, TValue> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiDoubleHashFactory, TDeamortizedCuckooHashingCfg_D2>); + + TCuckooD4<TKey, TValue, THashFactory> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D4>); + TCuckooD4<TKey, TValue> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiQuadrupleHashFactory, TDeamortizedCuckooHashingCfg_D4>); + + TCuckooD6<TKey, TValue, THashFactory> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D6>); + TCuckooD6<TKey, TValue> = class(TDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiSixfoldHashFactory, TDeamortizedCuckooHashingCfg_D6>); + + TObjectCuckooD2<TKey, TValue, THashFactory> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D2>); + TObjectCuckooD2<TKey, TValue> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiDoubleHashFactory, TDeamortizedCuckooHashingCfg_D2>); + + TObjectCuckooD4<TKey, TValue, THashFactory> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D4>); + TObjectCuckooD4<TKey, TValue> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiQuadrupleHashFactory, TDeamortizedCuckooHashingCfg_D4>); + + TObjectCuckooD6<TKey, TValue, THashFactory> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, THashFactory, TDeamortizedCuckooHashingCfg_D6>); + TObjectCuckooD6<TKey, TValue> = class(TObjectDeamortizedDArrayCuckooMap<TKey, TValue, TDelphiSixfoldHashFactory, TDeamortizedCuckooHashingCfg_D6>); + + // for normal programmers to normal use =) + TDictionary<TKey, TValue> = class(TOpenAddressingLP<TKey, TValue>); + TObjectDictionary<TKey, TValue> = class(TObjectOpenAddressingLP<TKey, TValue>); + + TFastHashMap<TKey, TValue> = class(TCuckooD2<TKey, TValue>); + TFastObjectHashMap<TKey, TValue> = class(TObjectCuckooD2<TKey, TValue>); + + THashMap<TKey, TValue> = class(TCuckooD4<TKey, TValue>); + TObjectHashMap<TKey, TValue> = class(TObjectCuckooD4<TKey, TValue>); + +var + EmptyRecord: TEmptyRecord; diff --git a/References/DelphiAST/Source/FreePascalSupport/IOUtils.pas b/References/DelphiAST/Source/FreePascalSupport/IOUtils.pas new file mode 100644 index 000000000..a5bdb75cd --- /dev/null +++ b/References/DelphiAST/Source/FreePascalSupport/IOUtils.pas @@ -0,0 +1,22 @@ +// Dummy implementation of IOUtils in order to be able to compile Delphi AST with FPC +unit IOUtils; + +interface + +uses + SysUtils; + +type + TPath = class + public + class function Combine(const Path1, Path2: string): string; inline; static; + end; + +implementation + +class function TPath.Combine(const Path1, Path2: string): string; +begin + Result := ConcatPaths([Path1, Path2]); +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Source/SimpleParser/SimpleParser.Lexer.Types.pas b/References/DelphiAST/Source/SimpleParser/SimpleParser.Lexer.Types.pas new file mode 100644 index 000000000..ceaff9514 --- /dev/null +++ b/References/DelphiAST/Source/SimpleParser/SimpleParser.Lexer.Types.pas @@ -0,0 +1,327 @@ +{--------------------------------------------------------------------------- +The contents of this file are subject to the Mozilla Public License Version +1.1 (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at +http://www.mozilla.org/NPL/NPL-1_1Final.html + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for +the specific language governing rights and limitations under the License. + +The Original Code is: mwPasLexTypes, released November 14, 1999. + +The Initial Developer of the Original Code is Martin Waldenburg +unit CastaliaPasLexTypes; + +----------------------------------------------------------------------------} + +unit SimpleParser.Lexer.Types; + +{$IFDEF FPC}{$MODE DELPHI}{$ENDIF} + +interface + +uses + SysUtils, + TypInfo; + +{$INCLUDE SimpleParser.inc} + +{$IFNDEF D14_NEWER} +type + TArray<T> = array of T; +{$ENDIF} + +type + TMessageEventType = (meError, meNotSupported); + + TMessageEvent = procedure(Sender: TObject; const Typ: TMessageEventType; + const Msg: string; X, Y: Integer) of object; + + TCommentState = (csAnsi, csBor, csNo); + + TTokenPoint = packed record + X: Integer; + Y: Integer; + LineSeq: Integer; + end; + + TptTokenKind = ( + ptAbort, + ptAbsolute, + ptAbstract, + ptAdd, + ptAddressOp, + ptAlign, + ptAmpersand, + ptAnd, + ptAnsiComment, + ptAnsiString, + ptArray, + ptAs, + ptAsciiChar, + ptAsm, + ptAssembler, + ptAssign, + ptAt, + ptAutomated, + ptBegin, + ptBoolean, + ptBorComment, + ptBraceClose, + ptBraceOpen, + ptBreak, + ptByte, + ptByteBool, + ptCardinal, + ptCase, + ptCdecl, + ptChar, + ptClass, + ptClassForward, + ptClassFunction, + ptClassProcedure, + ptColon, + ptComma, + ptComp, + ptCompDirect, + ptConst, + ptConstructor, + ptContains, + ptContinue, + ptCRLF, + ptCRLFCo, + ptCurrency, + ptDefault, + ptDefineDirect, + ptDeprecated, + ptDestructor, + ptDispid, + ptDispinterface, + ptDiv, + ptDo, + ptDotDot, + ptDouble, + ptDoubleAddressOp, + ptDownto, + ptDWORD, + ptDynamic, + ptElse, + ptElseDirect, + ptEnd, + ptEndIfDirect, + ptEqual, + ptError, + ptExcept, + ptExit, + ptExport, + ptExports, + ptExtended, + ptExternal, + ptFar, + ptFile, + ptFinal, + ptExperimental, + ptDelayed, + ptFinalization, + ptFinally, + ptFloat, + ptFor, + ptForward, + ptFunction, + ptGoto, + ptGreater, + ptGreaterEqual, + ptHalt, + ptHelper, + ptIdentifier, + ptIf, + ptIfDirect, + ptIfEndDirect, + ptElseIfDirect, + ptIfDefDirect, + ptIfNDefDirect, + ptIfOptDirect, + ptImplementation, + ptImplements, + ptIn, + ptIncludeDirect, + ptIndex, + ptInherited, + ptInitialization, + ptInline, + ptInt64, + ptInteger, + ptIntegerConst, + ptInterface, + ptIs, + ptLabel, + ptLibrary, + ptLocal, + ptLongBool, + ptLongint, + ptLongword, + ptLower, + ptLowerEqual, + ptMessage, + ptMinus, + ptMod, + ptName, + ptNear, + ptNil, + ptNodefault, + ptNone, + ptNoreturn, + ptNot, + ptNotEqual, + ptNull, + ptObject, + ptOf, + ptOleVariant, + ptOn, + ptOperator, + ptOr, + ptOut, + ptOverload, + ptOverride, + ptPackage, + ptPacked, + ptPascal, + ptPChar, + ptPlatform, + ptPlus, + ptPoint, + ptPointerSymbol, + ptPrivate, + ptProcedure, + ptProgram, + ptProperty, + ptProtected, + ptPublic, + ptPublished, + ptRaise, + ptRead, + ptReadonly, + ptReal, + ptReal48, + ptRecord, + ptReference, + ptRegister, + ptReintroduce, + ptRemove, + ptRepeat, + ptRequires, + ptResident, + ptResourceDirect, + ptResourcestring, + ptRoundClose, + ptRoundOpen, + ptRunError, + ptSafeCall, + ptScopedEnumsDirect, + ptSealed, + ptSemiColon, + ptSet, + ptShl, + ptShortint, + ptShortString, + ptShr, + ptSingle, + ptSlash, + ptSlashesComment, + ptSmallint, + ptSpace, + ptSquareClose, + ptSquareOpen, + ptStar, + ptStatic, + ptStdcall, + ptStored, + ptStrict, + ptString, + ptStringConst, + ptStringDQConst, + ptStringresource, + ptSymbol, + ptThen, + ptThreadvar, + ptTo, + ptTry, + ptType, + ptUndefDirect, + ptUnit, + ptUnknown, + ptUnsafe, + ptUntil, + ptUses, + ptVar, + ptVarargs, + ptVariant, + ptVirtual, + ptWhile, + ptWideChar, + ptWideString, + ptWith, + ptWord, + ptWordBool, + ptWrite, + ptWriteonly, + ptXor); + + TmwPasLexStatus = record + CommentState: TCommentState; + ExID: TptTokenKind; + LineNumber: Integer; + LinePos: Integer; + Origin: PChar; + RunPos: Integer; + TokenPos: Integer; + TokenID: TptTokenKind; + end; + + EIncludeError = class(Exception); + IIncludeHandler = interface + ['{C5F20740-41D2-43E9-8321-7FE5E3AA83B6}'] + function GetIncludeFileContent(const ParentFileName, IncludeName: string; + out Content: string; out FileName: string): Boolean; + end; + +function TokenName(Value: TptTokenKind): string; +function ptTokenName(Value: TptTokenKind): string; +function IsTokenIDJunk(const aTokenID: TptTokenKind): Boolean; + +implementation + +function TokenName(Value: TptTokenKind): string; +begin + Result := Copy(ptTokenName(Value), 3, MaxInt); +end; + +function ptTokenName(Value: TptTokenKind): string; +begin + result := GetEnumName(TypeInfo(TptTokenKind), Integer(Value)); +end; + +function IsTokenIDJunk(const aTokenID: TptTokenKind): Boolean; +begin + Result := aTokenID in [ + ptAnsiComment, + ptBorComment, + ptCRLF, + ptCRLFCo, + ptSlashesComment, + ptSpace, + ptIfDirect, + ptElseDirect, + ptIfEndDirect, + ptElseIfDirect, + ptIfDefDirect, + ptIfNDefDirect, + ptEndIfDirect, + ptIfOptDirect, + ptDefineDirect, + ptScopedEnumsDirect, + ptUndefDirect]; +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Source/SimpleParser/SimpleParser.Lexer.pas b/References/DelphiAST/Source/SimpleParser/SimpleParser.Lexer.pas new file mode 100644 index 000000000..57bb887cd --- /dev/null +++ b/References/DelphiAST/Source/SimpleParser/SimpleParser.Lexer.pas @@ -0,0 +1,3076 @@ +{----------------------------------------------------------------------------- +The contents of this file are subject to the Mozilla Public License Version +1.1 (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at +http://www.mozilla.org/NPL/NPL-1_1Final.html + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for +the specific language governing rights and limitations under the License. + +The Original Code is: mwPasLex.PAS, released August 17, 1999. + +The Initial Developer of the Original Code is Martin Waldenburg +(Martin.Waldenburg@T-Online.de). +Portions created by Martin Waldenburg are Copyright (C) 1998, 1999 Martin +Waldenburg. +All Rights Reserved. + +Contributor(s): James Jacobson, LaKraven Studios Ltd, Roman Yankovsky +(This list is ALPHABETICAL) + +Last Modified: mm/dd/yyyy +Current Version: 2.25 + +Notes: This program is a very fast Pascal tokenizer. I'd like to invite the +Delphi community to develop it further and to create a fully featured Object +Pascal parser. + +Modification history: + +LaKraven Studios Ltd, January 2015: + +- Cleaned up version-specifics up to XE8 +- Fixed all warnings & hints + +Daniel Rolf between 20010723 and 20020116 + +Made ready for Delphi 6 + +platform +deprecated +varargs +local + +Known Issues: +-----------------------------------------------------------------------------} + +unit SimpleParser.Lexer; + +{$IFDEF FPC}{$MODE DELPHI}{$ENDIF} + +{$I SimpleParser.inc} + +interface + +uses + SysUtils, Classes, Character, + {$IFDEF FPC} + Generics.Collections, + {$ENDIF} + SimpleParser.Lexer.Types; + +{$IFDEF FPC} +const + CompilerVersion = 0; + RTLVersion = 0; +{$ENDIF} + +var + Identifiers: array[#0..#127] of ByteBool; + mHashTable: array[#0..#127] of Integer; + +type + TmwBasePasLex = class; + TDirectiveEvent = procedure(Sender: TmwBasePasLex) of object; + TCommentEvent = procedure(Sender: TObject; const Text: string) of object; + + PDefineRec = ^TDefineRec; + TDefineRec = record + Defined: Boolean; + StartCount: Integer; + Next: PDefineRec; + end; + + PBufferRec = ^TBufferRec; + TBufferRec = record + Buf: PChar; + Run: Integer; + SharedBuffer: Boolean; + LineNumber: Integer; + LinePos: Integer; + FileName: string; + Next: PBufferRec; + end; + + TmwBasePasLex = class(TObject) + private + FCommentState: TCommentState; + FProcTable: array[#0..#127] of procedure of object; + FBuffer: PBufferRec; + RunAhead: Integer; + TempRun: Integer; + BufferSize: integer; + FIdentFuncTable: array[0..191] of function: TptTokenKind of object; + FTokenPos: Integer; + FTokenLine: Integer; + FTokenLinePos: Integer; + FTokenID: TptTokenKind; + FExID: TptTokenKind; + FOnMessage: TMessageEvent; + FOnCompDirect: TDirectiveEvent; + FOnElseDirect: TDirectiveEvent; + FOnEndIfDirect: TDirectiveEvent; + FOnIfDefDirect: TDirectiveEvent; + FOnIfNDefDirect: TDirectiveEvent; + FOnResourceDirect: TDirectiveEvent; + FOnIncludeDirect: TDirectiveEvent; + FOnDefineDirect: TDirectiveEvent; + FOnIfOptDirect: TDirectiveEvent; + FOnIfDirect: TDirectiveEvent; + FOnIfEndDirect: TDirectiveEvent; + FOnElseIfDirect: TDirectiveEvent; + FOnUnDefDirect: TDirectiveEvent; + FDirectiveParamOrigin: PChar; + FAsmCode: Boolean; + FDefines: TArray<string>; + FDefineStack: Integer; + FTopDefineRec: PDefineRec; + FUseDefines: Boolean; + FScopedEnums: Boolean; + FIncludeHandler: IIncludeHandler; + FOnComment: TCommentEvent; + FLineSeq: Integer; + + function KeyHash: Integer; + function KeyComp(const aKey: string): Boolean; + function Func9: tptTokenKind; + function Func15: TptTokenKind; + function Func19: TptTokenKind; + function Func20: TptTokenKind; + function Func21: TptTokenKind; + function Func23: TptTokenKind; + function Func25: TptTokenKind; + function Func27: TptTokenKind; + function Func28: TptTokenKind; + function Func29: TptTokenKind; + function Func30: TptTokenKind; + function Func32: TptTokenKind; + function Func33: TptTokenKind; + function Func35: TptTokenKind; + function Func36: TptTokenKind; + function Func37: TptTokenKind; + function Func38: TptTokenKind; + function Func39: TptTokenKind; + function Func40: TptTokenKind; + function Func41: TptTokenKind; + function Func42: TptTokenKind; + function Func43: TptTokenKind; + function Func44: TptTokenKind; + function Func45: TptTokenKind; + function Func46: TptTokenKind; + function Func47: TptTokenKind; + function Func49: TptTokenKind; + function Func52: TptTokenKind; + function Func54: TptTokenKind; + function Func55: TptTokenKind; + function Func56: TptTokenKind; + function Func57: TptTokenKind; + function Func58: TptTokenKind; + function Func59: TptTokenKind; + function Func60: TptTokenKind; + function Func61: TptTokenKind; + function Func62: TptTokenKind; + function Func63: TptTokenKind; + function Func64: TptTokenKind; + function Func65: TptTokenKind; + function Func66: TptTokenKind; + function Func69: TptTokenKind; + function Func71: TptTokenKind; + function Func72: TptTokenKind; + function Func73: TptTokenKind; + function Func75: TptTokenKind; + function Func76: TptTokenKind; + function Func78: TptTokenKind; + function Func79: TptTokenKind; + function Func81: TptTokenKind; + function Func84: TptTokenKind; + function Func85: TptTokenKind; + function Func86: TptTokenKind; + function Func87: TptTokenKind; + function Func88: TptTokenKind; + function Func89: TptTokenKind; + function Func91: TptTokenKind; + function Func92: TptTokenKind; + function Func94: TptTokenKind; + function Func95: TptTokenKind; + function Func96: TptTokenKind; + function Func97: TptTokenKind; + function Func98: TptTokenKind; + function Func99: TptTokenKind; + function Func100: TptTokenKind; + function Func101: TptTokenKind; + function Func102: TptTokenKind; + function Func103: TptTokenKind; + function Func104: TptTokenKind; + function Func105: TptTokenKind; + function Func106: TptTokenKind; + function Func107: TptTokenKind; + function Func108: TptTokenKind; + function Func112: TptTokenKind; + function Func117: TptTokenKind; + function Func123: TptTokenKind; + function Func125: TptTokenKind; + function Func126: TptTokenKind; + function Func127: TptTokenKind; + function Func128: TptTokenKind; + function Func129: TptTokenKind; + function Func130: TptTokenKind; + function Func132: TptTokenKind; + function Func133: TptTokenKind; + function Func136: TptTokenKind; + function Func141: TptTokenKind; + function Func142: TptTokenKind; + function Func143: TptTokenKind; + function Func166: TptTokenKind; + function Func167: TptTokenKind; + function Func168: TptTokenKind; + function Func191: TptTokenKind; + function AltFunc: TptTokenKind; + procedure InitIdent; + function GetPosXY: TTokenPoint; + function IdentKind: TptTokenKind; + procedure MakeMethodTables; + procedure AddressOpProc; + procedure AmpersandOpProc; + procedure AsciiCharProc; + procedure AnsiProc; + procedure BinaryIntegerProc; + procedure BorProc; + procedure BraceCloseProc; + procedure BraceOpenProc; + procedure ColonProc; + procedure CommaProc; + procedure CRProc; + procedure EqualProc; + procedure GreaterProc; + procedure IdentProc; + procedure IntegerProc; + procedure LFProc; + procedure LowerProc; + procedure MinusProc; + procedure NullProc; + procedure NumberProc; + procedure PlusProc; + procedure PointerSymbolProc; + procedure PointProc; + procedure RoundCloseProc; + procedure RoundOpenProc; + procedure SemiColonProc; + procedure SlashProc; + procedure SpaceProc; + procedure SquareCloseProc; + procedure SquareOpenProc; + procedure StarProc; + procedure StringProc; + procedure StringDQProc; + procedure SymbolProc; + procedure UnknownProc; + function GetToken: string; inline; + function GetTokenLen: Integer; inline; + function GetCompilerDirective: string; + function GetDirectiveKind: TptTokenKind; + function GetDirectiveParam: string; + function GetStringContent: string; + function GetIsJunk: Boolean; + function GetIsSpace: Boolean; + function GetIsOrdIdent: Boolean; + function GetIsRealType: Boolean; + function GetIsStringType: Boolean; + function GetIsVariantType: Boolean; + function GetIsAddOperator: Boolean; + function GetIsMulOperator: Boolean; + function GetIsRelativeOperator: Boolean; + function GetIsCompilerDirective: Boolean; + function GetIsOrdinalType: Boolean; + function GetGenID: TptTokenKind; + + procedure EnterDefineBlock(ADefined: Boolean); + procedure ExitDefineBlock; + procedure CloneDefinesFrom(ALexer: TmwBasePasLex); + procedure DoProcTable(AChar: Char); + function IsIdentifiers(AChar: Char): Boolean; inline; + function HashValue(AChar: Char): Integer; + function EvaluateComparison(AValue1: Extended; const AOper: String; AValue2: Extended): Boolean; + function EvaluateConditionalExpression(const AParams: String): Boolean; + procedure IncludeFile; + function GetIncludeFileNameFromToken(const IncludeToken: string): string; + function GetOrigin: string; + function GetRunPos: Integer; + procedure SetRunPos(const Value: Integer); + procedure SetSharedBuffer(SharedBuffer: PBufferRec); + procedure DisposeBuffer(Buf: PBufferRec); + function GetFileName: string; + procedure UpdateScopedEnums; + procedure DoOnComment(const CommentText: string); + protected + procedure SetOrigin(const NewValue: string); virtual; + public + constructor Create; + destructor Destroy; override; + function CharAhead: Char; + procedure Next; + procedure NextNoJunk; + procedure NextNoSpace; + procedure Init; + procedure InitFrom(ALexer: TmwBasePasLex); + function FirstInLine: Boolean; + + procedure AddDefine(const ADefine: string); + procedure RemoveDefine(const ADefine: string); + function IsDefined(const ADefine: string): Boolean; + procedure ClearDefines; + procedure InitDefinesDefinedByCompiler; + + property Buffer: PBufferRec read FBuffer; + property CompilerDirective: string read GetCompilerDirective; + property DirectiveParam: string read GetDirectiveParam; + property IsJunk: Boolean read GetIsJunk; + property IsSpace: Boolean read GetIsSpace; + property Origin: string read GetOrigin write SetOrigin; + property PosXY: TTokenPoint read GetPosXY; + property RunPos: Integer read GetRunPos write SetRunPos; + property Token: string read GetToken; + property TokenLen: Integer read GetTokenLen; + property TokenPos: Integer read FTokenPos; + property TokenID: TptTokenKind read FTokenID; + property ExID: TptTokenKind read FExID; + property GenID: TptTokenKind read GetGenID; + property StringContent: string read GetStringContent; + property IsOrdIdent: Boolean read GetIsOrdIdent; + property IsOrdinalType: Boolean read GetIsOrdinalType; + property IsRealType: Boolean read GetIsRealType; + property IsStringType: Boolean read GetIsStringType; + property IsVariantType: Boolean read GetIsVariantType; + property IsRelativeOperator: Boolean read GetIsRelativeOperator; + property IsAddOperator: Boolean read GetIsAddOperator; + property IsMulOperator: Boolean read GetIsMulOperator; + property IsCompilerDirective: Boolean read GetIsCompilerDirective; + property OnComment: TCommentEvent read FOnComment write FOnComment; + property OnMessage: TMessageEvent read FOnMessage write FOnMessage; + property OnCompDirect: TDirectiveEvent read FOnCompDirect write FOnCompDirect; + property OnDefineDirect: TDirectiveEvent read FOnDefineDirect write FOnDefineDirect; + property OnElseDirect: TDirectiveEvent read FOnElseDirect write FOnElseDirect; + property OnEndIfDirect: TDirectiveEvent read FOnEndIfDirect write FOnEndIfDirect; + property OnIfDefDirect: TDirectiveEvent read FOnIfDefDirect write FOnIfDefDirect; + property OnIfNDefDirect: TDirectiveEvent read FOnIfNDefDirect write FOnIfNDefDirect; + property OnIfOptDirect: TDirectiveEvent read FOnIfOptDirect write FOnIfOptDirect; + property OnIncludeDirect: TDirectiveEvent read FOnIncludeDirect write FOnIncludeDirect; + property OnIfDirect: TDirectiveEvent read FOnIfDirect write FOnIfDirect; + property OnIfEndDirect: TDirectiveEvent read FOnIfEndDirect write FOnIfEndDirect; + property OnElseIfDirect: TDirectiveEvent read FOnElseIfDirect write FOnElseIfDirect; + property OnResourceDirect: TDirectiveEvent read FOnResourceDirect write FOnResourceDirect; + property OnUnDefDirect: TDirectiveEvent read FOnUnDefDirect write FOnUnDefDirect; + property AsmCode: Boolean read FAsmCode write FAsmCode; + property DirectiveParamOrigin: PChar read FDirectiveParamOrigin; + property UseDefines: Boolean read FUseDefines write FUseDefines; + property ScopedEnums: Boolean read FScopedEnums; + property IncludeHandler: IIncludeHandler read FIncludeHandler write FIncludeHandler; + property FileName: string read GetFileName; + end; + + TmwPasLex = class(TmwBasePasLex) + private + FAheadLex: TmwBasePasLex; + function GetAheadExID: TptTokenKind; + function GetAheadGenID: TptTokenKind; + function GetAheadToken: string; + function GetAheadTokenID: TptTokenKind; + protected + procedure SetOrigin(const NewValue: string); override; + public + constructor Create; + destructor Destroy; override; + procedure InitAhead; + procedure AheadNext; + property AheadLex: TmwBasePasLex read FAheadLex; + property AheadToken: string read GetAheadToken; + property AheadTokenID: TptTokenKind read GetAheadTokenID; + property AheadExID: TptTokenKind read GetAheadExID; + property AheadGenID: TptTokenKind read GetAheadGenID; + end; + +implementation + +uses + StrUtils; + +type + TmwPasLexExpressionEvaluation = (leeNone, leeAnd, leeOr); + +procedure MakeIdentTable; +var + I, J: Char; +begin + for I := #0 to #127 do + begin + case I of + '_', '0'..'9', 'a'..'z', 'A'..'Z': Identifiers[I] := True; + else + Identifiers[I] := False; + end; + J := UpperCase(I)[1]; + case I of + 'a'..'z', 'A'..'Z', '_': mHashTable[I] := Ord(J) - 64; + else + mHashTable[Char(I)] := 0; + end; + end; +end; + +function TmwBasePasLex.CharAhead: Char; +begin + RunAhead := FBuffer.Run; + while (FBuffer.Buf[RunAhead] > #0) and (FBuffer.Buf[RunAhead] < #33) do + Inc(RunAhead); + Result := FBuffer.Buf[RunAhead]; +end; + +procedure TmwBasePasLex.ClearDefines; +var + Frame: PDefineRec; +begin + while FTopDefineRec <> nil do + begin + Frame := FTopDefineRec; + FTopDefineRec := Frame^.Next; + Dispose(Frame); + end; + FDefines := nil; + FDefineStack := 0; +end; + +procedure TmwBasePasLex.CloneDefinesFrom(ALexer: TmwBasePasLex); +var + Frame, LastFrame, SourceFrame: PDefineRec; +begin + ClearDefines; + FDefines := Copy(ALexer.FDefines); + FDefineStack := ALexer.FDefineStack; + + Frame := nil; + LastFrame := nil; + SourceFrame := ALexer.FTopDefineRec; + while SourceFrame <> nil do + begin + New(Frame); + if FTopDefineRec = nil then + FTopDefineRec := Frame + else + LastFrame^.Next := Frame; + Frame^.Defined := SourceFrame^.Defined; + Frame^.StartCount := SourceFrame^.StartCount; + LastFrame := Frame; + + SourceFrame := SourceFrame^.Next; + end; + if Frame <> nil then + Frame^.Next := nil; +end; + +function TmwBasePasLex.GetPosXY: TTokenPoint; +begin + Result.Y := FTokenLine + 1; + Result.X := FTokenPos - FTokenLinePos + 1; +end; + +function TmwBasePasLex.GetRunPos: Integer; +begin + Result := FBuffer.Run; +end; + +procedure TmwBasePasLex.InitIdent; +var + I: Integer; +begin + for I := 0 to 191 do + case I of + 9: FIdentFuncTable[I] := Func9; + 15: FIdentFuncTable[I] := Func15; + 19: FIdentFuncTable[I] := Func19; + 20: FIdentFuncTable[I] := Func20; + 21: FIdentFuncTable[I] := Func21; + 23: FIdentFuncTable[I] := Func23; + 25: FIdentFuncTable[I] := Func25; + 27: FIdentFuncTable[I] := Func27; + 28: FIdentFuncTable[I] := Func28; + 29: FIdentFuncTable[I] := Func29; + 30: FIdentFuncTable[I] := Func30; + 32: FIdentFuncTable[I] := Func32; + 33: FIdentFuncTable[I] := Func33; + 35: FIdentFuncTable[I] := Func35; + 36: FIdentFuncTable[I] := Func36; + 37: FIdentFuncTable[I] := Func37; + 38: FIdentFuncTable[I] := Func38; + 39: FIdentFuncTable[I] := Func39; + 40: FIdentFuncTable[I] := Func40; + 41: FIdentFuncTable[I] := Func41; + 42: FIdentFuncTable[I] := Func42; + 43: FIdentFuncTable[I] := Func43; + 44: FIdentFuncTable[I] := Func44; + 45: FIdentFuncTable[I] := Func45; + 46: FIdentFuncTable[I] := Func46; + 47: FIdentFuncTable[I] := Func47; + 49: FIdentFuncTable[I] := Func49; + 52: FIdentFuncTable[I] := Func52; + 54: FIdentFuncTable[I] := Func54; + 55: FIdentFuncTable[I] := Func55; + 56: FIdentFuncTable[I] := Func56; + 57: FIdentFuncTable[I] := Func57; + 58: FIdentFuncTable[I] := Func58; + 59: FIdentFuncTable[I] := Func59; + 60: FIdentFuncTable[I] := Func60; + 61: FIdentFuncTable[I] := Func61; + 62: FIdentFuncTable[I] := Func62; + 63: FIdentFuncTable[I] := Func63; + 64: FIdentFuncTable[I] := Func64; + 65: FIdentFuncTable[I] := Func65; + 66: FIdentFuncTable[I] := Func66; + 69: FIdentFuncTable[I] := Func69; + 71: FIdentFuncTable[I] := Func71; + 72: FIdentFuncTable[I] := Func72; + 73: FIdentFuncTable[I] := Func73; + 75: FIdentFuncTable[I] := Func75; + 76: FIdentFuncTable[I] := Func76; + 78: FIdentFuncTable[I] := Func78; + 79: FIdentFuncTable[I] := Func79; + 81: FIdentFuncTable[I] := Func81; + 84: FIdentFuncTable[I] := Func84; + 85: FIdentFuncTable[I] := Func85; + 86: FIdentFuncTable[I] := Func86; + 87: FIdentFuncTable[I] := Func87; + 88: FIdentFuncTable[I] := Func88; + 89: FIdentFuncTable[I] := Func89; + 91: FIdentFuncTable[I] := Func91; + 92: FIdentFuncTable[I] := Func92; + 94: FIdentFuncTable[I] := Func94; + 95: FIdentFuncTable[I] := Func95; + 96: FIdentFuncTable[I] := Func96; + 97: FIdentFuncTable[I] := Func97; + 98: FIdentFuncTable[I] := Func98; + 99: FIdentFuncTable[I] := Func99; + 100: FIdentFuncTable[I] := Func100; + 101: FIdentFuncTable[I] := Func101; + 102: FIdentFuncTable[I] := Func102; + 103: FIdentFuncTable[I] := Func103; + 104: FIdentFuncTable[I] := Func104; + 105: FIdentFuncTable[I] := Func105; + 106: FIdentFuncTable[I] := Func106; + 107: FIdentFuncTable[I] := Func107; + 108: FIdentFuncTable[I] := Func108; + 112: FIdentFuncTable[I] := Func112; + 117: FIdentFuncTable[I] := Func117; + 123: FIdentFuncTable[I] := Func123; + 125: FIdentFuncTable[I] := Func125; + 126: FIdentFuncTable[I] := Func126; + 127: FIdentFuncTable[I] := Func127; + 128: FIdentFuncTable[I] := Func128; + 129: FIdentFuncTable[I] := Func129; + 130: FIdentFuncTable[I] := Func130; + 132: FIdentFuncTable[I] := Func132; + 133: FIdentFuncTable[I] := Func133; + 136: FIdentFuncTable[I] := Func136; + 141: FIdentFuncTable[I] := Func141; + 142: FIdentFuncTable[I] := Func142; + 143: FIdentFuncTable[I] := Func143; + 166: FIdentFuncTable[I] := Func166; + 167: FIdentFuncTable[I] := Func167; + 168: FIdentFuncTable[I] := Func168; + 191: FIdentFuncTable[I] := Func191; + else + FIdentFuncTable[I] := AltFunc; + end; +end; + +function TmwBasePasLex.KeyHash: Integer; +begin + Result := 0; + while IsIdentifiers(FBuffer.Buf[FBuffer.Run]) do + begin + Inc(Result, HashValue(FBuffer.Buf[FBuffer.Run])); + Inc(FBuffer.Run); + end; +end; + +function TmwBasePasLex.KeyComp(const aKey: string): Boolean; +var + I: Integer; + Temp: PChar; +begin + if Length(aKey) = TokenLen then + begin + Temp := FBuffer.Buf + FTokenPos; + Result := True; + for i := 1 to TokenLen do + begin + if mHashTable[Temp^] <> mHashTable[aKey[i]] then + begin + Result := False; + Break; + end; + Inc(Temp); + end; + end + else + Result := False; +end; + +function TmwBasePasLex.Func9: tptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Add') then + FExID := ptAdd; +end; + +function TmwBasePasLex.Func15: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('If') then Result := ptIf; +end; + +function TmwBasePasLex.Func19: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Do') then Result := ptDo else + if KeyComp('And') then Result := ptAnd; +end; + +function TmwBasePasLex.Func20: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('As') then Result := ptAs; +end; + +function TmwBasePasLex.Func21: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Of') then Result := ptOf else + if KeyComp('At') then FExID := ptAt; +end; + +function TmwBasePasLex.Func23: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('End') then Result := ptEnd else + if KeyComp('In') then Result := ptIn; +end; + +function TmwBasePasLex.Func25: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Far') then FExID := ptFar; +end; + +function TmwBasePasLex.Func27: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Cdecl') then FExID := ptCdecl; +end; + +function TmwBasePasLex.Func28: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Read') then FExID := ptRead else + if KeyComp('Case') then Result := ptCase else + if KeyComp('Is') then Result := ptIs; +end; + +function TmwBasePasLex.Func29: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('On') then FExID := ptOn; +end; + +function TmwBasePasLex.Func30: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Char') then FExID := ptChar; +end; + +function TmwBasePasLex.Func32: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('File') then Result := ptFile else + if KeyComp('Label') then Result := ptLabel else + if KeyComp('Mod') then Result := ptMod; +end; + +function TmwBasePasLex.Func33: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Or') then Result := ptOr else + if KeyComp('Name') then FExID := ptName else + if KeyComp('Asm') then Result := ptAsm; +end; + +function TmwBasePasLex.Func35: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Nil') then Result := ptNil else + if KeyComp('To') then Result := ptTo else + if KeyComp('Div') then Result := ptDiv; +end; + +function TmwBasePasLex.Func36: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Real') then FExID := ptReal else + if KeyComp('Real48') then FExID := ptReal48; +end; + +function TmwBasePasLex.Func37: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Begin') then Result := ptBegin else + if KeyComp('Break') then FExID := ptBreak; +end; + +function TmwBasePasLex.Func38: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Near') then FExID := ptNear; +end; + +function TmwBasePasLex.Func39: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('For') then Result := ptFor else + if KeyComp('Shl') then Result := ptShl; +end; + +function TmwBasePasLex.Func40: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Packed') then Result := ptPacked; +end; + +function TmwBasePasLex.Func41: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Var') then Result := ptVar else + if KeyComp('Else') then Result := ptElse else + if KeyComp('Halt') then FExID := ptHalt; +end; + +function TmwBasePasLex.Func42: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Final') then + FExID := ptFinal; //TODO: Is this supposed to be an ExID? +end; + +function TmwBasePasLex.Func43: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Int64') then FExID := ptInt64 + else if KeyComp('local') then FExID := ptLocal + else if KeyComp('align') then FExID := ptAlign; +end; + +function TmwBasePasLex.Func44: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Set') then Result := ptSet else + if KeyComp('Package') then FExID := ptPackage; +end; + +function TmwBasePasLex.Func45: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Shr') then Result := ptShr; +end; + +function TmwBasePasLex.Func46: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('PChar') then FExID := ptPChar else + if KeyComp('Sealed') then Result := ptSealed; +end; + +function TmwBasePasLex.Func47: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Then') then Result := ptThen else + if KeyComp('Comp') then FExID := ptComp; +end; + +function TmwBasePasLex.Func49: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Not') then Result := ptNot; +end; + +function TmwBasePasLex.Func52: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Byte') then FExID := ptByte else + if KeyComp('Raise') then Result := ptRaise else + if KeyComp('Pascal') then FExID := ptPascal; +end; + +function TmwBasePasLex.Func54: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Class') then Result := ptClass; +end; + +function TmwBasePasLex.Func55: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Object') then Result := ptObject; +end; + +function TmwBasePasLex.Func56: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Index') then FExID := ptIndex else + if KeyComp('Out') then FExID := ptOut else // bug in Delphi's documentation: OUT is a directive + if KeyComp('Abort') then FExID := ptAbort else + if KeyComp('Delayed') then FExID := ptDelayed; +end; + +function TmwBasePasLex.Func57: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('While') then Result := ptWhile else + if KeyComp('Xor') then Result := ptXor else + if KeyComp('Goto') then Result := ptGoto; +end; + +function TmwBasePasLex.Func58: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Exit') then FExID := ptExit; +end; + +function TmwBasePasLex.Func59: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Safecall') then FExID := ptSafecall else + if KeyComp('Double') then FExID := ptDouble; +end; + +function TmwBasePasLex.Func60: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('With') then Result := ptWith else + if KeyComp('Word') then FExID := ptWord; +end; + +function TmwBasePasLex.Func61: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Dispid') then FExID := ptDispid; +end; + +function TmwBasePasLex.Func62: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Cardinal') then FExID := ptCardinal; +end; + +function TmwBasePasLex.Func63: TptTokenKind; +begin + Result := ptIdentifier; + case FBuffer.Buf[FTokenPos] of + 'P', 'p': if KeyComp('Public') then FExID := ptPublic; + 'A', 'a': if KeyComp('Array') then Result := ptArray; + 'T', 't': if KeyComp('Try') then Result := ptTry; + 'R', 'r': if KeyComp('Record') then Result := ptRecord; + 'I', 'i': if KeyComp('Inline') then + begin + Result := ptInline; + FExID := ptInline; + end; + end; +end; + +function TmwBasePasLex.Func64: TptTokenKind; +begin + Result := ptIdentifier; + case FBuffer.Buf[FTokenPos] of + 'B', 'b': if KeyComp('Boolean') then FExID := ptBoolean; + 'D', 'd': if KeyComp('DWORD') then FExID := ptDWORD; + 'U', 'u': if KeyComp('Uses') then Result := ptUses else + if KeyComp('Unit') then Result := ptUnit; + 'H', 'h': if KeyComp('Helper') then FExID := ptHelper; + end; +end; + +function TmwBasePasLex.Func65: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Repeat') then Result := ptRepeat; +end; + +function TmwBasePasLex.Func66: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Single') then FExID := ptSingle else + if KeyComp('Type') then Result := ptType else + if KeyComp('Unsafe') then Result := ptUnsafe; +end; + +function TmwBasePasLex.Func69: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Default') then FExID := ptDefault else + if KeyComp('Dynamic') then FExID := ptDynamic else + if KeyComp('Message') then FExID := ptMessage; +end; + +function TmwBasePasLex.Func71: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('WideChar') then FExID := ptWideChar else + if KeyComp('Stdcall') then FExID := ptStdcall else + if KeyComp('Const') then Result := ptConst; +end; + +function TmwBasePasLex.Func72: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Static') then FExID := ptStatic; +end; + +function TmwBasePasLex.Func73: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Except') then Result := ptExcept; +end; + +function TmwBasePasLex.Func75: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Write') then FExID := ptWrite; +end; + +function TmwBasePasLex.Func76: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Until') then Result := ptUntil; +end; + +function TmwBasePasLex.Func78: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Integer') then FExID := ptInteger else + if KeyComp('Remove') then FExID := ptRemove; +end; + +function TmwBasePasLex.Func79: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Finally') then Result := ptFinally else + if KeyComp('Reference') then FExID := ptReference; +end; + +function TmwBasePasLex.Func81: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Extended') then FExID := ptExtended else + if KeyComp('Stored') then FExID := ptStored else + if KeyComp('Interface') then Result := ptInterface else + if KeyComp('Deprecated') then FExID := ptDeprecated; +end; + +function TmwBasePasLex.Func84: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Abstract') then FExID := ptAbstract; +end; + +function TmwBasePasLex.Func85: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Library') then Result := ptLibrary else + if KeyComp('Forward') then FExID := ptForward else + if KeyComp('Variant') then FExID := ptVariant; +end; + +function TmwBasePasLex.Func87: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('String') then Result := ptString; +end; + +function TmwBasePasLex.Func88: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Program') then Result := ptProgram; +end; + +function TmwBasePasLex.Func89: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Strict') then FExID := ptStrict; +end; + +function TmwBasePasLex.Func91: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Downto') then Result := ptDownto else + if KeyComp('Private') then FExID := ptPrivate else + if KeyComp('Longint') then FExID := ptLongint; +end; + +function TmwBasePasLex.Func92: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Inherited') then Result := ptInherited else + if KeyComp('LongBool') then FExID := ptLongBool else + if KeyComp('Overload') then FExID := ptOverload; +end; + +function TmwBasePasLex.Func94: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Resident') then FExID := ptResident else + if KeyComp('Readonly') then FExID := ptReadonly else + if KeyComp('Assembler') then FExID := ptAssembler; +end; + +function TmwBasePasLex.Func95: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Contains') then FExID := ptContains else + if KeyComp('Absolute') then FExID := ptAbsolute; +end; + +function TmwBasePasLex.Func96: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('ByteBool') then FExID := ptByteBool else + if KeyComp('Override') then FExID := ptOverride else + if KeyComp('Published') then FExID := ptPublished; +end; + +function TmwBasePasLex.Func97: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Threadvar') then Result := ptThreadvar; +end; + +function TmwBasePasLex.Func98: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Export') then FExID := ptExport else + if KeyComp('Nodefault') then FExID := ptNodefault; +end; + +function TmwBasePasLex.Func99: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('External') then FExID := ptExternal; +end; + +function TmwBasePasLex.Func100: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Automated') then FExID := ptAutomated else + if KeyComp('Smallint') then FExID := ptSmallint; +end; + +function TmwBasePasLex.Func101: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Register') then FExID := ptRegister else + if KeyComp('Platform') then FExID := ptPlatform else + if KeyComp('Continue') then FExID := ptContinue; +end; + +function TmwBasePasLex.Func102: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Function') then Result := ptFunction; +end; + +function TmwBasePasLex.Func103: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Virtual') then FExID := ptVirtual; +end; + +function TmwBasePasLex.Func104: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('WordBool') then FExID := ptWordBool; +end; + +function TmwBasePasLex.Func105: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Procedure') then Result := ptProcedure; +end; + +function TmwBasePasLex.Func106: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Protected') then FExID := ptProtected; +end; + +function TmwBasePasLex.Func107: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Currency') then FExID := ptCurrency; +end; + +function TmwBasePasLex.Func108: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Longword') then FExID := ptLongword else + if KeyComp('Operator') then FExID := ptOperator; +end; + +function TmwBasePasLex.Func112: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Requires') then FExID := ptRequires; +end; + +function TmwBasePasLex.Func117: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Exports') then Result := ptExports else + if KeyComp('OleVariant') then FExID := ptOleVariant; +end; + +function TmwBasePasLex.Func123: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Shortint') then FExID := ptShortint; +end; + +function TmwBasePasLex.Func125: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('noreturn') then FExID := ptNoreturn; +end; + +function TmwBasePasLex.Func126: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Implements') then FExID := ptImplements; +end; + +function TmwBasePasLex.Func127: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Runerror') then FExID := ptRunError; +end; + +function TmwBasePasLex.Func128: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('WideString') then FExID := ptWideString; +end; + +function TmwBasePasLex.Func129: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Dispinterface') then Result := ptDispinterface +end; + +function TmwBasePasLex.Func130: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('AnsiString') then FExID := ptAnsiString; +end; + +function TmwBasePasLex.Func132: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Reintroduce') then FExID := ptReintroduce; +end; + +function TmwBasePasLex.Func133: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Property') then Result := ptProperty; +end; + +function TmwBasePasLex.Func136: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Finalization') then Result := ptFinalization; +end; + +function TmwBasePasLex.Func141: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Writeonly') then FExID := ptWriteonly; +end; + +function TmwBasePasLex.Func142: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('experimental') then FExID := ptExperimental; +end; + +function TmwBasePasLex.Func143: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Destructor') then Result := ptDestructor; +end; + +function TmwBasePasLex.Func166: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Constructor') then Result := ptConstructor else + if KeyComp('Implementation') then Result := ptImplementation; +end; + +function TmwBasePasLex.Func167: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('ShortString') then FExID := ptShortString; +end; + +function TmwBasePasLex.Func168: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Initialization') then Result := ptInitialization; +end; + +function TmwBasePasLex.Func191: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Resourcestring') then Result := ptResourcestring else + if KeyComp('Stringresource') then FExID := ptStringresource; +end; + +function TmwBasePasLex.AltFunc: TptTokenKind; +begin + Result := ptIdentifier; +end; + +function TmwBasePasLex.IdentKind: TptTokenKind; +var + HashKey: Integer; +begin + HashKey := KeyHash; + if HashKey < 192 then + Result := FIdentFuncTable[HashKey] + else + Result := ptIdentifier; +end; + +procedure TmwBasePasLex.MakeMethodTables; +var + I: Char; +begin + for I := #0 to #127 do + case I of + #0: FProcTable[I] := NullProc; + #10: FProcTable[I] := LFProc; + #13: FProcTable[I] := CRProc; + #1..#9, #11, #12, #14..#32: FProcTable[I] := SpaceProc; + '#': FProcTable[I] := AsciiCharProc; + '$': FProcTable[I] := IntegerProc; + '%': FProcTable[I] := BinaryIntegerProc; + #39: FProcTable[I] := StringProc; + '0'..'9': FProcTable[I] := NumberProc; + 'A'..'Z', 'a'..'z', '_': FProcTable[I] := IdentProc; + '{': FProcTable[I] := BraceOpenProc; + '}': FProcTable[I] := BraceCloseProc; + '!', '"', '&', '('..'/', ':'..'@', '['..'^', '`', '~': + begin + case I of + '(': FProcTable[I] := RoundOpenProc; + ')': FProcTable[I] := RoundCloseProc; + '*': FProcTable[I] := StarProc; + '+': FProcTable[I] := PlusProc; + ',': FProcTable[I] := CommaProc; + '-': FProcTable[I] := MinusProc; + '.': FProcTable[I] := PointProc; + '/': FProcTable[I] := SlashProc; + ':': FProcTable[I] := ColonProc; + ';': FProcTable[I] := SemiColonProc; + '<': FProcTable[I] := LowerProc; + '=': FProcTable[I] := EqualProc; + '>': FProcTable[I] := GreaterProc; + '@': FProcTable[I] := AddressOpProc; + '[': FProcTable[I] := SquareOpenProc; + ']': FProcTable[I] := SquareCloseProc; + '^': FProcTable[I] := PointerSymbolProc; + '"': FProcTable[I] := StringDQProc; + '&': FProcTable[I] := AmpersandOpProc; + else + FProcTable[I] := SymbolProc; + end; + end; + else + FProcTable[I] := UnknownProc; + end; +end; + +constructor TmwBasePasLex.Create; +begin + inherited Create; + InitIdent; + MakeMethodTables; + FExID := ptUnKnown; + + FUseDefines := True; + FScopedEnums := False; + FTopDefineRec := nil; + ClearDefines; + + New(FBuffer); + FillChar(FBuffer^, SizeOf(TBufferRec), 0); +end; + +destructor TmwBasePasLex.Destroy; +begin + if not FBuffer.SharedBuffer then + FreeMem(FBuffer.Buf); + + Dispose(FBuffer); + + ClearDefines; //If we don't do this, we get a memory leak + inherited Destroy; +end; + +procedure TmwBasePasLex.DisposeBuffer(Buf: PBufferRec); +begin + if Assigned(Buf.Buf) and not Buf.SharedBuffer then + FreeMem(Buf.Buf); + Dispose(Buf); +end; + +procedure TmwBasePasLex.DoOnComment(const CommentText: string); +begin + if not FUseDefines or (FDefineStack = 0) then + FOnComment(Self, CommentText); +end; + +procedure TmwBasePasLex.DoProcTable(AChar: Char); +begin + if Ord(AChar) <= 127 then + FProcTable[AChar] + else + begin + IdentProc; + end; +end; + +procedure TmwBasePasLex.SetOrigin(const NewValue: string); +begin + BufferSize := (Length(NewValue) + 1) * SizeOf(Char); + + GetMem(FBuffer.Buf, BufferSize); + StrPCopy(FBuffer.Buf, NewValue); + + Init; + Next; +end; + +procedure TmwBasePasLex.SetRunPos(const Value: Integer); +begin + FBuffer.Run := Value; + Next; +end; + +procedure TmwBasePasLex.SetSharedBuffer(SharedBuffer: PBufferRec); +var + NextBuffer: PBufferRec; +begin + while Assigned(FBuffer.Next) do + begin + NextBuffer := FBuffer; + FBuffer := FBuffer.Next; + DisposeBuffer(NextBuffer); + end; + + if not FBuffer.SharedBuffer and Assigned(FBuffer.Buf) then + FreeMem(FBuffer.Buf); + + FBuffer.Buf := SharedBuffer.Buf; + FBuffer.Run := SharedBuffer.Run; + FBuffer.LineNumber := SharedBuffer.LineNumber; + FBuffer.LinePos := SharedBuffer.LinePos; + FBuffer.SharedBuffer := True; + + Next; +end; + +procedure TmwBasePasLex.AddDefine(const ADefine: string); +var + len: Integer; +begin + len := Length(FDefines); + SetLength(FDefines, len + 1); + FDefines[len] := ADefine; +end; + +procedure TmwBasePasLex.AddressOpProc; +begin + case FBuffer.Buf[FBuffer.Run + 1] of + '@': + begin + FTokenID := ptDoubleAddressOp; + Inc(FBuffer.Run, 2); + end; + else + begin + FTokenID := ptAddressOp; + Inc(FBuffer.Run); + end; + end; +end; + +procedure TmwBasePasLex.AsciiCharProc; +begin + FTokenID := ptAsciiChar; + Inc(FBuffer.Run); + if FBuffer.Buf[FBuffer.Run] = '$' then + begin + Inc(FBuffer.Run); + while CharInSet(FBuffer.Buf[FBuffer.Run], ['0'..'9', 'A'..'F', 'a'..'f']) do Inc(FBuffer.Run); + end else + begin +{$IFDEF SUPPORTS_INTRINSIC_HELPERS} + while Char(FBuffer.Buf[FBuffer.Run]).IsDigit do +{$ELSE} + while IsDigit(FBuffer.Buf[FBuffer.Run]) do +{$ENDIF} + Inc(FBuffer.Run); + end; +end; + +procedure TmwBasePasLex.BraceCloseProc; +begin + Inc(FBuffer.Run); + FTokenID := ptError; + if Assigned(FOnMessage) then + FOnMessage(Self, meError, 'Illegal character', PosXY.X, PosXY.Y); +end; + +procedure TmwBasePasLex.BinaryIntegerProc; +begin + Inc(FBuffer.Run); + FTokenID := ptIntegerConst; + while CharInSet(FBuffer.Buf[FBuffer.Run], ['0', '1', '_']) do + Inc(FBuffer.Run); +end; + +procedure TmwBasePasLex.BorProc; +var + BeginRun: Integer; + CommentText: string; +begin + FTokenID := ptBorComment; + case FBuffer.Buf[FBuffer.Run] of + #0: + begin + NullProc; + if Assigned(FOnMessage) then + FOnMessage(Self, meError, 'Unexpected file end', PosXY.X, PosXY.Y); + Exit; + end; + end; + + BeginRun := FBuffer.Run; + + while FBuffer.Buf[FBuffer.Run] <> #0 do + case FBuffer.Buf[FBuffer.Run] of + '}': + begin + FCommentState := csNo; + Inc(FBuffer.Run); + Break; + end; + #10: + begin + Inc(FLineSeq); + Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; + end; + #13: + begin + Inc(FLineSeq); + Inc(FBuffer.Run); + if FBuffer.Buf[FBuffer.Run] = #10 then Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; + end; + else + Inc(FBuffer.Run); + end; + + if Assigned(FOnComment) then + begin + SetString(CommentText, PChar(@FBuffer.Buf[BeginRun]), FBuffer.Run - BeginRun - 1); + DoOnComment(CommentText); + end; +end; + +procedure TmwBasePasLex.BraceOpenProc; +var + BeginRun: Integer; + CommentText: string; +begin + case FBuffer.Buf[FBuffer.Run + 1] of + '$': FTokenID := GetDirectiveKind; + else + begin + FTokenID := ptBorComment; + FCommentState := csBor; + end; + end; + + Inc(FBuffer.Run); + BeginRun := FBuffer.Run; + while FBuffer.Buf[FBuffer.Run] <> #0 do + case FBuffer.Buf[FBuffer.Run] of + '}': + begin + FCommentState := csNo; + Inc(FBuffer.Run); + Break; + end; + #10: + begin + Inc(FLineSeq); + Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; + end; + #13: + begin + Inc(FLineSeq); + Inc(FBuffer.Run); + if FBuffer.Buf[FBuffer.Run] = #10 then Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; + end; + else + Inc(FBuffer.Run); + end; + case FTokenID of + PtBorComment: + begin + if Assigned(FOnComment) then + begin + SetString(CommentText, PChar(@FBuffer.Buf[BeginRun]), FBuffer.Run - BeginRun - 1); + DoOnComment(CommentText); + end; + end; + PtCompDirect: + begin + if Assigned(FOnCompDirect) then + FOnCompDirect(Self); + end; + PtDefineDirect: + begin + if FUseDefines and (FDefineStack = 0) then + AddDefine(DirectiveParam); + if Assigned(FOnDefineDirect) then + FOnDefineDirect(Self); + end; + PtElseDirect: + begin + if FUseDefines then + begin + if FTopDefineRec <> nil then + begin + if FTopDefineRec^.Defined then + Inc(FDefineStack) + else + if FDefineStack > 0 then + Dec(FDefineStack); + end; + end; + if Assigned(FOnElseDirect) then + FOnElseDirect(Self); + end; + PtEndIfDirect: + begin + if FUseDefines then + ExitDefineBlock; + if Assigned(FOnEndIfDirect) then + FOnEndIfDirect(Self); + end; + PtIfDefDirect: + begin + if FUseDefines then + EnterDefineBlock(IsDefined(DirectiveParam)); + if Assigned(FOnIfDefDirect) then + FOnIfDefDirect(Self); + end; + PtIfNDefDirect: + begin + if FUseDefines then + EnterDefineBlock(not IsDefined(DirectiveParam)); + if Assigned(FOnIfNDefDirect) then + FOnIfNDefDirect(Self); + end; + PtIfOptDirect: + begin + if FUseDefines then + EnterDefineBlock(False); + if Assigned(FOnIfOptDirect) then + FOnIfOptDirect(Self); + end; + PtIfDirect: + begin + if FUseDefines then + EnterDefineBlock(EvaluateConditionalExpression(DirectiveParam)); + if Assigned(FOnIfDirect) then + FOnIfDirect(Self); + end; + PtIfEndDirect: + begin + if FUseDefines then + ExitDefineBlock; + if Assigned(FOnIfEndDirect) then + FOnIfEndDirect(Self); + end; + PtElseIfDirect: + begin + if FUseDefines then + begin + if FTopDefineRec <> nil then + begin + if FTopDefineRec^.Defined then + FDefineStack := FTopDefineRec.StartCount + 1 + else + begin + FDefineStack := FTopDefineRec.StartCount; + if EvaluateConditionalExpression(DirectiveParam) then + FTopDefineRec^.Defined := True + else + FDefineStack := FTopDefineRec.StartCount + 1 + end; + end; + end; + if Assigned(FOnElseIfDirect) then + FOnElseIfDirect(Self); + end; + PtIncludeDirect: + begin +// if Assigned(FOnIncludeDirect) then +// FOnIncludeDirect(Self); + if Assigned(FIncludeHandler) and (FDefineStack = 0) then + IncludeFile + else + Next; + end; + PtResourceDirect: + begin + if Assigned(FOnResourceDirect) then + FOnResourceDirect(Self); + end; + PtScopedEnumsDirect: + begin + UpdateScopedEnums; + end; + PtUndefDirect: + begin + if FUseDefines and (FDefineStack = 0) then + RemoveDefine(DirectiveParam); + if Assigned(FOnUnDefDirect) then + FOnUnDefDirect(Self); + end; + end; +end; + +function TmwBasePasLex.EvaluateComparison(AValue1: Extended; const AOper: String; AValue2: Extended): Boolean; +begin + if AOper = '=' then + Result := AValue1 = AValue2 + else if AOper = '<>' then + Result := AValue1 <> AValue2 + else if AOper = '<' then + Result := AValue1 < AValue2 + else if AOper = '<=' then + Result := AValue1 <= AValue2 + else if AOper = '>' then + Result := AValue1 > AValue2 + else if AOper = '>=' then + Result := AValue1 >= AValue2 + else + Result := False; +end; + +function TmwBasePasLex.EvaluateConditionalExpression(const AParams: String): Boolean; +var + LParams: String; + LDefine: String; + LEvaluation: TmwPasLexExpressionEvaluation; + LIsComVer: Boolean; + LIsRtlVer: Boolean; + LOper: string; + LValue: Integer; + p: Integer; +begin + { TODO : Expand support for <=> evaluations (complicated to do). Expand support for NESTED expressions } + LEvaluation := leeNone; + LParams := TrimLeft(AParams); + LIsComVer := Pos('COMPILERVERSION', LParams) = 1; + LIsRtlVer := Pos('RTLVERSION', LParams) = 1; + if LIsComVer or LIsRtlVer then //simple parser which covers most frequent use cases + begin + Result := False; + if LIsComVer then + Delete(LParams, 1, Length('COMPILERVERSION')); + if LIsRtlVer then + Delete(LParams, 1, Length('RTLVERSION')); + while (LParams <> '') and (LParams[1] = ' ') do + Delete(LParams, 1, 1); + p := Pos(' ', LParams); + if p > 0 then + begin + LOper := Copy(LParams, 1, p-1); + Delete(LParams, 1, p); + while (LParams <> '') and (LParams[1] = ' ') do + Delete(LParams, 1, 1); + p := Pos(' ', LParams); + if p = 0 then + p := Length(LParams) + 1; + if TryStrToInt(Copy(LParams, 1, p-1), LValue) then + begin + Delete(LParams, 1, p); + while (LParams <> '') and (LParams[1] = ' ') do + Delete(LParams, 1, 1); + if LParams = '' then + if LIsComVer then + Result := EvaluateComparison(CompilerVersion, LOper, LValue) + else if LIsRtlVer then + Result := EvaluateComparison(RTLVersion, LOper, LValue); + end; + end; + end else + if (Pos('DEFINED(', LParams) = 1) or (Pos('NOT DEFINED(', LParams) = 1) then + begin + Result := True; // Optimistic + while (Pos('DEFINED(', LParams) = 1) or (Pos('NOT DEFINED(', LParams) = 1) do + begin + if Pos('DEFINED(', LParams) = 1 then + begin + LDefine := Copy(LParams, 9, Pos(')', LParams) - 9); + LParams := TrimLeft(Copy(LParams, 10 + Length(LDefine), Length(AParams) - (9 + Length(LDefine)))); + case LEvaluation of + leeNone: Result := IsDefined(LDefine); + leeAnd: Result := Result and IsDefined(LDefine); + leeOr: Result := Result or IsDefined(LDefine); + end; + end + else if Pos('NOT DEFINED(', LParams) = 1 then + begin + LDefine := Copy(LParams, 13, Pos(')', LParams) - 13); + LParams := TrimLeft(Copy(LParams, 14 + Length(LDefine), Length(AParams) - (13 + Length(LDefine)))); + case LEvaluation of + leeNone: Result := (not IsDefined(LDefine)); + leeAnd: Result := Result and (not IsDefined(LDefine)); + leeOr: Result := Result or (not IsDefined(LDefine)); + end; + end; + // Determine next Evaluation + if Pos('AND ', LParams) = 1 then + begin + LEvaluation := leeAnd; + LParams := TrimLeft(Copy(LParams, 4, Length(LParams) - 3)); + end + else if Pos('OR ', LParams) = 1 then + begin + LEvaluation := leeOr; + LParams := TrimLeft(Copy(LParams, 3, Length(LParams) - 2)); + end; + end; + end else + Result := False; +end; + +procedure TmwBasePasLex.ColonProc; +begin + case FBuffer.Buf[FBuffer.Run + 1] of + '=': + begin + Inc(FBuffer.Run, 2); + FTokenID := ptAssign; + end; + else + begin + Inc(FBuffer.Run); + FTokenID := ptColon; + end; + end; +end; + +procedure TmwBasePasLex.CommaProc; +begin + Inc(FBuffer.Run); + FTokenID := ptComma; +end; + +procedure TmwBasePasLex.CRProc; +begin + case FCommentState of + csBor: FTokenID := ptCRLFCo; + csAnsi: FTokenID := ptCRLFCo; + else + FTokenID := ptCRLF; + end; + + case FBuffer.Buf[FBuffer.Run + 1] of + #10: Inc(FBuffer.Run, 2); + else + Inc(FBuffer.Run); + end; + Inc(FLineSeq); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; +end; + +procedure TmwBasePasLex.EnterDefineBlock(ADefined: Boolean); +var + StackFrame: PDefineRec; +begin + New(StackFrame); + StackFrame^.Next := FTopDefineRec; + StackFrame^.Defined := ADefined; + StackFrame^.StartCount := FDefineStack; + FTopDefineRec := StackFrame; + if not ADefined then + Inc(FDefineStack); +end; + +procedure TmwBasePasLex.EqualProc; +begin + Inc(FBuffer.Run); + FTokenID := ptEqual; +end; + +procedure TmwBasePasLex.ExitDefineBlock; +var + StackFrame: PDefineRec; +begin + StackFrame := FTopDefineRec; + if StackFrame <> nil then + begin + FDefineStack := StackFrame^.StartCount; + FTopDefineRec := StackFrame^.Next; + Dispose(StackFrame); + end; +end; + +procedure TmwBasePasLex.GreaterProc; +begin + case FBuffer.Buf[FBuffer.Run + 1] of + '=': + begin + Inc(FBuffer.Run, 2); + FTokenID := ptGreaterEqual; + end; + else + begin + Inc(FBuffer.Run); + FTokenID := ptGreater; + end; + end; +end; + +function TmwBasePasLex.HashValue(AChar: Char): Integer; +begin + if AChar <= #127 then + Result := mHashTable[FBuffer.Buf[FBuffer.Run]] + else + Result := Ord(AChar); +end; + +procedure TmwBasePasLex.IdentProc; +begin + FTokenID := IdentKind; +end; + +procedure TmwBasePasLex.IntegerProc; +begin + Inc(FBuffer.Run); + FTokenID := ptIntegerConst; + while CharInSet(FBuffer.Buf[FBuffer.Run], ['0'..'9', 'A'..'F', 'a'..'f', '_']) do + Inc(FBuffer.Run); +end; + +function TmwBasePasLex.IsDefined(const ADefine: string): Boolean; +var + i: Integer; +begin + for i := 0 to High(FDefines) do + if SameText(FDefines[i], ADefine) then + Exit(True); + Result := False; +end; + +function TmwBasePasLex.IsIdentifiers(AChar: Char): Boolean; +begin + {$IF DECLARED(TCharHelper)} + Result := AChar.IsLetterOrDigit or (AChar = '_') + or ((Ord(AChar) > 127) and not AChar.IsHighSurrogate and not AChar.IsLowSurrogate); + {$ELSE} + // assuming Delphi identifier may include letters, digits, underscore symbol + // and any character over 127 except surrogates + Result := TCharacter.IsLetterOrDigit(AChar) or (AChar = '_') + or ((Ord(AChar) > 127) and not TCharacter.IsHighSurrogate(AChar) and not TCharacter.IsLowSurrogate(AChar)); + {$IFEND} +end; + +procedure TmwBasePasLex.LFProc; +begin + case FCommentState of + csBor: FTokenID := ptCRLFCo; + csAnsi: FTokenID := ptCRLFCo; + else + FTokenID := ptCRLF; + end; + Inc(FLineSeq); + Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; +end; + +procedure TmwBasePasLex.LowerProc; +begin + case FBuffer.Buf[FBuffer.Run + 1] of + '=': + begin + Inc(FBuffer.Run, 2); + FTokenID := ptLowerEqual; + end; + '>': + begin + Inc(FBuffer.Run, 2); + FTokenID := ptNotEqual; + end + else + begin + Inc(FBuffer.Run); + FTokenID := ptLower; + end; + end; +end; + +procedure TmwBasePasLex.MinusProc; +begin + Inc(FBuffer.Run); + FTokenID := ptMinus; +end; + +procedure TmwBasePasLex.NullProc; +var + OldBuffer: PBufferRec; +begin + if Assigned(FBuffer.Next) then + begin + OldBuffer := FBuffer; + FBuffer := FBuffer.Next; + DisposeBuffer(OldBuffer); + + Next; + end else + FTokenID := ptNull; +end; + +procedure TmwBasePasLex.NumberProc; +begin + Inc(FBuffer.Run); + FTokenID := ptIntegerConst; + while CharInSet(FBuffer.Buf[FBuffer.Run], ['0'..'9', '.', 'e', 'E', '_']) do + begin + case FBuffer.Buf[FBuffer.Run] of + '.': + if FBuffer.Buf[FBuffer.Run + 1] = '.' then + Break + else + FTokenID := ptFloat + end; + Inc(FBuffer.Run); + end; +end; + +procedure TmwBasePasLex.PlusProc; +begin + Inc(FBuffer.Run); + FTokenID := ptPlus; +end; + +procedure TmwBasePasLex.PointerSymbolProc; +const + PointerChars = ['a'..'z', 'A'..'Z', '\', '!', '"', '#', '$', '%', '&', '''', + '?', '@', '_', '`', '|', '}', '~']; + // TODO: support ']', '), ''*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '{', '^', '(', '[' +begin + Inc(FBuffer.Run); + FTokenID := ptPointerSymbol; + + //This is a wierd Pascal construct that rarely appears, but needs to be + //supported. ^M is a valid char reference (#13, in this case) + if CharInSet(FBuffer.Buf[FBuffer.Run], PointerChars) and not IsIdentifiers(FBuffer.Buf[FBuffer.Run+1]) then + begin + Inc(FBuffer.Run); + FTokenID := ptAsciiChar; + end; +end; + +procedure TmwBasePasLex.PointProc; +begin + case FBuffer.Buf[FBuffer.Run + 1] of + '.': + begin + Inc(FBuffer.Run, 2); + FTokenID := ptDotDot; + end; + ')': + begin + Inc(FBuffer.Run, 2); + FTokenID := ptSquareClose; + end; + else + begin + Inc(FBuffer.Run); + FTokenID := ptPoint; + end; + end; +end; + +procedure Delete(var values: TArray<string>; index: Integer); +var + len: Integer; + tailCount: Integer; +begin + len := Length(values); + if len = 0 then + Exit; + values[index] := ''; + tailCount := len - (index + 1); + if tailCount > 0 then + Move(values[index + 1], values[index], SizeOf(string) * tailCount); + Pointer(values[len - 1]) := nil; // do not trigger string refcounting as we moved it + SetLength(values, len - 1); +end; + +procedure TmwBasePasLex.RemoveDefine(const ADefine: string); +var + i: Integer; +begin + for i := High(FDefines) downto 0 do + if SameText(FDefines[i], ADefine) then + Delete(FDefines, i); +end; + +procedure TmwBasePasLex.RoundCloseProc; +begin + Inc(FBuffer.Run); + FTokenID := ptRoundClose; +end; + +procedure TmwBasePasLex.AnsiProc; +var + BeginRun: Integer; + CommentText: string; +begin + FTokenID := ptAnsiComment; + case FBuffer.Buf[FBuffer.Run] of + #0: + begin + NullProc; + if Assigned(FOnMessage) then + FOnMessage(Self, meError, 'Unexpected file end', PosXY.X, PosXY.Y); + Exit; + end; + end; + + BeginRun := FBuffer.Run + 1; + + while FBuffer.Buf[FBuffer.Run] <> #0 do + case FBuffer.Buf[FBuffer.Run] of + '*': + if FBuffer.Buf[FBuffer.Run + 1] = ')' then + begin + FCommentState := csNo; + Inc(FBuffer.Run, 2); + Break; + end + else Inc(FBuffer.Run); + #10: + begin + Inc(FLineSeq); + Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; + end; + #13: + begin + Inc(FLineSeq); + Inc(FBuffer.Run); + if FBuffer.Buf[FBuffer.Run] = #10 then Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; + end; + else + Inc(FBuffer.Run); + end; + + if Assigned(FOnComment) then + begin + SetString(CommentText, PChar(@FBuffer.Buf[BeginRun]), FBuffer.Run - BeginRun - 2); + DoOnComment(CommentText); + end; +end; + +procedure TmwBasePasLex.RoundOpenProc; +var + BeginRun: Integer; + CommentText: string; +begin + BeginRun := FBuffer.Run + 2; + Inc(FBuffer.Run); + case FBuffer.Buf[FBuffer.Run] of + '*': + begin + FTokenID := ptAnsiComment; + if FBuffer.Buf[FBuffer.Run + 1] = '$' then + FTokenID := GetDirectiveKind + else + FCommentState := csAnsi; + Inc(FBuffer.Run); + while FBuffer.Buf[FBuffer.Run] <> #0 do + case FBuffer.Buf[FBuffer.Run] of + '*': + if FBuffer.Buf[FBuffer.Run + 1] = ')' then + begin + FCommentState := csNo; + Inc(FBuffer.Run, 2); + Break; + end + else + Inc(FBuffer.Run); + #10: + begin + Inc(FLineSeq); + Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; + end; + #13: + begin + Inc(FLineSeq); + Inc(FBuffer.Run); + if FBuffer.Buf[FBuffer.Run] = #10 then Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; + end; + else + Inc(FBuffer.Run); + end; + end; + '.': + begin + Inc(FBuffer.Run); + FTokenID := ptSquareOpen; + end; + else + FTokenID := ptRoundOpen; + end; + case FTokenID of + PtAnsiComment: + begin + if Assigned(FOnComment) then + begin + SetString(CommentText, PChar(@FBuffer.Buf[BeginRun]), FBuffer.Run - BeginRun - 2); + DoOnComment(CommentText); + end; + end; + PtCompDirect: + begin + if Assigned(FOnCompDirect) then + FOnCompDirect(Self); + end; + PtDefineDirect: + begin + if Assigned(FOnDefineDirect) then + FOnDefineDirect(Self); + end; + PtElseDirect: + begin + if Assigned(FOnElseDirect) then + FOnElseDirect(Self); + end; + PtEndIfDirect: + begin + if Assigned(FOnEndIfDirect) then + FOnEndIfDirect(Self); + end; + PtIfDefDirect: + begin + if Assigned(FOnIfDefDirect) then + FOnIfDefDirect(Self); + end; + PtIfNDefDirect: + begin + if Assigned(FOnIfNDefDirect) then + FOnIfNDefDirect(Self); + end; + PtIfOptDirect: + begin + if Assigned(FOnIfOptDirect) then + FOnIfOptDirect(Self); + end; + PtIncludeDirect: + begin + if Assigned(FIncludeHandler) then + IncludeFile; + end; + PtResourceDirect: + begin + if Assigned(FOnResourceDirect) then + FOnResourceDirect(Self); + end; + PtScopedEnumsDirect: + begin + UpdateScopedEnums; + end; + PtUndefDirect: + begin + if Assigned(FOnUnDefDirect) then + FOnUnDefDirect(Self); + end; + end; +end; + +procedure TmwBasePasLex.SemiColonProc; +begin + Inc(FBuffer.Run); + FTokenID := ptSemiColon; +end; + +procedure TmwBasePasLex.SlashProc; +var + BeginRun: Integer; + CommentText: string; +begin + case FBuffer.Buf[FBuffer.Run + 1] of + '/': + begin + Inc(FBuffer.Run, 2); + + BeginRun := FBuffer.Run; + + FTokenID := ptSlashesComment; + while FBuffer.Buf[FBuffer.Run] <> #0 do + begin + case FBuffer.Buf[FBuffer.Run] of + #10, #13: Break; + end; + Inc(FBuffer.Run); + end; + + if Assigned(FOnComment) then + begin + SetString(CommentText, PChar(@FBuffer.Buf[BeginRun]), FBuffer.Run - BeginRun); + DoOnComment(CommentText); + end; + end; + else + begin + Inc(FBuffer.Run); + FTokenID := ptSlash; + end; + end; +end; + +procedure TmwBasePasLex.SpaceProc; +begin + Inc(FBuffer.Run); + FTokenID := ptSpace; + while CharInSet(FBuffer.Buf[FBuffer.Run], [#1..#9, #11, #12, #14..#32]) do + Inc(FBuffer.Run); +end; + +procedure TmwBasePasLex.SquareCloseProc; +begin + Inc(FBuffer.Run); + FTokenID := ptSquareClose; +end; + +procedure TmwBasePasLex.SquareOpenProc; +begin + Inc(FBuffer.Run); + FTokenID := ptSquareOpen; +end; + +procedure TmwBasePasLex.StarProc; +begin + Inc(FBuffer.Run); + FTokenID := ptStar; +end; + +procedure TmwBasePasLex.StringProc; +var + StartQuoteCount, EndQuoteCount: Integer; + NewLine: Boolean; +begin + FTokenID := ptStringConst; + + StartQuoteCount := 0; + while FBuffer.Buf[FBuffer.Run] = #39 do + begin + StartQuoteCount := StartQuoteCount + 1; + Inc(FBuffer.Run); + end; + + if StartQuoteCount mod 2 = 0 then + Exit; + + if (StartQuoteCount > 1) and ((FBuffer.Buf[FBuffer.Run] = #10) or (FBuffer.Buf[FBuffer.Run] = #13)) then + begin // multiline string + NewLine := False; + repeat + case FBuffer.Buf[FBuffer.Run] of + #10: + begin + NewLine := True; + Inc(FLineSeq); + Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; + end; + #13: + begin + NewLine := True; + Inc(FLineSeq); + Inc(FBuffer.Run); + if FBuffer.Buf[FBuffer.Run] = #10 then Inc(FBuffer.Run); + Inc(FBuffer.LineNumber); + FBuffer.LinePos := FBuffer.Run; + end; + #0: + begin + if Assigned(FOnMessage) then + FOnMessage(Self, meError, 'Unterminated string', PosXY.X, PosXY.Y); + Break; + end; + #39: + begin + EndQuoteCount := 0; + while (FBuffer.Buf[FBuffer.Run] = #39) do + begin + Inc(EndQuoteCount); + Inc(FBuffer.Run); + end; + if EndQuoteCount = StartQuoteCount then + begin + if not NewLine and Assigned(FOnMessage) then + FOnMessage(Self, meError, 'Non-whitespace characters before closing quotes', PosXY.X, PosXY.Y); + Break; + end; + NewLine := False; + end; + else + if NewLine and (FBuffer.Buf[FBuffer.Run] <> #9) and (FBuffer.Buf[FBuffer.Run] <> #32) then + NewLine := False; + end; + Inc(FBuffer.Run); + until False; + end + else + begin // singleline string + repeat + Inc(FBuffer.Run); + case FBuffer.Buf[FBuffer.Run] of + #0, #10, #13: + begin + if Assigned(FOnMessage) then + FOnMessage(Self, meError, 'Unterminated string', PosXY.X, PosXY.Y); + Break; + end; + #39: + begin + while (FBuffer.Buf[FBuffer.Run] = #39) and (FBuffer.Buf[FBuffer.Run + 1] = #39) do + begin + Inc(FBuffer.Run, 2); + end; + end; + end; + until FBuffer.Buf[FBuffer.Run] = #39; + if FBuffer.Buf[FBuffer.Run] = #39 then + begin + Inc(FBuffer.Run); + if TokenLen = 3 then + begin + FTokenID := ptAsciiChar; + end; + end; + end; +end; + +procedure TmwBasePasLex.SymbolProc; +begin + Inc(FBuffer.Run); + FTokenID := ptSymbol; +end; + +procedure TmwBasePasLex.UnknownProc; +begin + Inc(FBuffer.Run); + FTokenID := ptUnknown; + if Assigned(FOnMessage) then + FOnMessage(Self, meError, 'Unknown Character', PosXY.X, PosXY.Y); +end; + +procedure TmwBasePasLex.Next; +begin + FExID := ptUnKnown; + FTokenPos := FBuffer.Run; + FTokenLine := FBuffer.LineNumber; + FTokenLinePos := FBuffer.LinePos; + case FCommentState of + csNo: DoProcTable(FBuffer.Buf[FBuffer.Run]); + csBor: BorProc; + csAnsi: AnsiProc; + end; +end; + +function TmwBasePasLex.GetIsJunk: Boolean; +begin + Result := IsTokenIDJunk(FTokenID) or (FUseDefines and (FDefineStack > 0) and (TokenID <> ptNull)); +end; + +function TmwBasePasLex.GetIsSpace: Boolean; +begin + Result := FTokenID in [ptCRLF, ptSpace]; +end; + +function TmwBasePasLex.GetToken: string; +begin + SetString(Result, FBuffer.Buf + FTokenPos, TokenLen); +end; + +function TmwBasePasLex.GetTokenLen: Integer; +begin + Result := FBuffer.Run - FTokenPos; +end; + +procedure TmwBasePasLex.NextNoJunk; +begin + repeat + Next; + until not IsJunk; +end; + +procedure TmwBasePasLex.NextNoSpace; +begin + repeat + Next; + until not IsSpace; +end; + +function TmwBasePasLex.FirstInLine: Boolean; +var + RunBack: Integer; +begin + Result := True; + if FTokenPos = 0 then Exit; + RunBack := FTokenPos; + Dec(RunBack); + while CharInSet(FBuffer.Buf[RunBack], [#1..#9, #11, #12, #14..#32]) do + Dec(RunBack); + if RunBack = 0 then Exit; + case FBuffer.Buf[RunBack] of + #10, #13: Exit; + else + begin + Result := False; + Exit; + end; + end; +end; + +function TmwBasePasLex.GetCompilerDirective: string; +var + DirectLen: Integer; +begin + if TokenID <> ptCompDirect then + Result := '' + else + case FBuffer.Buf[FTokenPos] of + '(': + begin + DirectLen := FBuffer.Run - FTokenPos - 4; + SetString(Result, (FBuffer.Buf + FTokenPos + 2), DirectLen); + Result := UpperCase(Result); + end; + '{': + begin + DirectLen := FBuffer.Run - FTokenPos - 2; + SetString(Result, (FBuffer.Buf + FTokenPos + 1), DirectLen); + Result := UpperCase(Result); + end; + end; +end; + +function TmwBasePasLex.GetDirectiveKind: TptTokenKind; +var + TempPos: Integer; +begin + case FBuffer.Buf[FTokenPos] of + '(': FBuffer.Run := FTokenPos + 3; + '{': FBuffer.Run := FTokenPos + 2; + end; + FDirectiveParamOrigin := FBuffer.Buf + FTokenPos; + TempPos := FTokenPos; + FTokenPos := FBuffer.Run; + case KeyHash of + 9: + if KeyComp('I') and (not CharInSet(FBuffer.Buf[FBuffer.Run], ['+', '-'])) then + Result := ptIncludeDirect else + Result := ptCompDirect; + 15: + if KeyComp('IF') then + Result := ptIfDirect else + Result := ptCompDirect; + 18: + if KeyComp('R') then + begin + if not CharInSet(FBuffer.Buf[FBuffer.Run], ['+', '-']) then + Result := ptResourceDirect else Result := ptCompDirect; + end else Result := ptCompDirect; + 30: + if KeyComp('IFDEF') then + Result := ptIfDefDirect else + Result := ptCompDirect; + 38: + if KeyComp('ENDIF') then + Result := ptEndIfDirect else + if KeyComp('IFEND') then + Result := ptIfEndDirect else + Result := ptCompDirect; + 41: + if KeyComp('ELSE') then + Result := ptElseDirect else + Result := ptCompDirect; + 43: + if KeyComp('DEFINE') then + Result := ptDefineDirect else + Result := ptCompDirect; + 44: + if KeyComp('IFNDEF') then + Result := ptIfNDefDirect else + Result := ptCompDirect; + 50: + if KeyComp('UNDEF') then + Result := ptUndefDirect else + Result := ptCompDirect; + 56: + if KeyComp('ELSEIF') then + Result := ptElseIfDirect else + Result := ptCompDirect; + 66: + if KeyComp('IFOPT') then + Result := ptIfOptDirect else + Result := ptCompDirect; + 68: + if KeyComp('INCLUDE') then + Result := ptIncludeDirect else + Result := ptCompDirect; + 104: + if KeyComp('Resource') then + Result := ptResourceDirect else + Result := ptCompDirect; + 134: + if KeyComp('SCOPEDENUMS') then + Result := ptScopedEnumsDirect else + Result := ptCompDirect; + else Result := ptCompDirect; + end; + FTokenPos := TempPos; + Dec(FBuffer.Run); +end; + +function TmwBasePasLex.GetDirectiveParam: string; +var + EndPos: Integer; + ParamLen: Integer; +begin + case FBuffer.Buf[FTokenPos] of + '(': + begin + TempRun := FTokenPos + 3; + EndPos := FBuffer.Run - 2; + end; + '{': + begin + TempRun := FTokenPos + 2; + EndPos := FBuffer.Run - 1; + end; + else + EndPos := 0; + end; + while IsIdentifiers(FBuffer.Buf[TempRun]) do + Inc(TempRun); + while CharInSet(FBuffer.Buf[TempRun], ['+', ',', '-']) do + begin + Inc(TempRun); + while IsIdentifiers(FBuffer.Buf[TempRun]) do + Inc(TempRun); + if CharInSet(FBuffer.Buf[TempRun - 1], ['+', ',', '-']) and (FBuffer.Buf[TempRun] = ' ') + then Inc(TempRun); + end; + + while CharInSet(FBuffer.Buf[TempRun], [' ', #9]) do Inc(TempRun); + while CharInSet(FBuffer.Buf[EndPos - 1], [' ', #9]) do Dec(EndPos); + + ParamLen := EndPos - TempRun; + SetString(Result, (FBuffer.Buf + TempRun), ParamLen); + Result := UpperCase(Result); +end; + +function TmwBasePasLex.GetFileName: string; +begin + Result := FBuffer.FileName; +end; + +function TmwBasePasLex.GetIncludeFileNameFromToken(const IncludeToken: string): string; +var + FileNameStartPos, CurrentPos: integer; + TrimmedToken: string; + QuotedFileName: Boolean; +begin + TrimmedToken := Trim(IncludeToken); + CurrentPos := 1; + while TrimmedToken[CurrentPos] > #32 do + inc(CurrentPos); + while TrimmedToken[CurrentPos] <= #32 do + inc(CurrentPos); + QuotedFileName := TrimmedToken[CurrentPos] = ''''; + if QuotedFileName then + inc(CurrentPos); + FileNameStartPos := CurrentPos; + while (TrimmedToken[CurrentPos] <> '}') + and (TrimmedToken[CurrentPos] <> '''') + and ((TrimmedToken[CurrentPos] > #32) or QuotedFileName) + do + inc(CurrentPos); + + Result := Copy(TrimmedToken, FileNameStartPos, CurrentPos - FileNameStartPos); +end; + +procedure TmwBasePasLex.IncludeFile; +var + IncludeName, IncludeDirective, Content, FileName: string; + NewBuffer: PBufferRec; +begin + IncludeDirective := Token; + IncludeName := GetIncludeFileNameFromToken(IncludeDirective); + + if FIncludeHandler.GetIncludeFileContent(FBuffer.FileName, IncludeName, Content, FileName) then + begin + Content := Content + #13#10; + + New(NewBuffer); + NewBuffer.SharedBuffer := False; + NewBuffer.Next := FBuffer; + NewBuffer.LineNumber := 0; + NewBuffer.LinePos := 0; + NewBuffer.Run := 0; + NewBuffer.FileName := FileName; + GetMem(NewBuffer.Buf, (Length(Content) + 1) * SizeOf(Char)); + StrPCopy(NewBuffer.Buf, Content); + NewBuffer.Buf[Length(Content)] := #0; + + FBuffer := NewBuffer; + end; + + Next; +end; + +procedure TmwBasePasLex.Init; +begin + FCommentState := csNo; + FBuffer.LineNumber := 0; + FBuffer.LinePos := 0; + FBuffer.Run := 0; + FLineSeq := 0; +end; + +procedure TmwBasePasLex.InitFrom(ALexer: TmwBasePasLex); +begin + SetSharedBuffer(ALexer.FBuffer); + FCommentState := ALexer.FCommentState; + FScopedEnums := ALexer.ScopedEnums; + FBuffer.Run := ALexer.RunPos; + FTokenID := ALexer.TokenID; + FExID := ALexer.ExID; + CloneDefinesFrom(ALexer); +end; + +procedure TmwBasePasLex.InitDefinesDefinedByCompiler; +begin + //Set up the defines that are defined by the compiler + {$IFDEF VER90} + AddDefine('VER90'); // 2 + {$ENDIF} + {$IFDEF VER100} + AddDefine('VER100'); // 3 + {$ENDIF} + {$IFDEF VER120} + AddDefine('VER120'); // 4 + {$ENDIF} + {$IFDEF VER130} + AddDefine('VER130'); // 5 + {$ENDIF} + {$IFDEF VER140} // 6 + AddDefine('VER140'); + {$ENDIF} + {$IFDEF VER150} // 7/7.1 + AddDefine('VER150'); + {$ENDIF} + {$IFDEF VER160} // 8 + AddDefine('VER160'); + {$ENDIF} + {$IFDEF VER170} // 2005 + AddDefine('VER170'); + {$ENDIF} + {$IFDEF VER180} // 2007 + AddDefine('VER180'); + {$ENDIF} + {$IFDEF VER185} // 2007 + AddDefine('VER185'); + {$ENDIF} + {$IFDEF VER190} // 2007.NET + AddDefine('VER190'); + {$ENDIF} + {$IFDEF CONDITIONALEXPRESSIONS} + {$IF COMPILERVERSION > 19.0} + AddDefine('VER' + IntToStr(Round(10*CompilerVersion))); + {$IFEND} + {$ENDIF} + {$IFDEF WIN32} + AddDefine('WIN32'); + {$ENDIF} + {$IFDEF WIN64} + AddDefine('WIN64'); + {$ENDIF} + {$IFDEF LINUX} + AddDefine('LINUX'); + {$ENDIF} + {$IFDEF LINUX32} + AddDefine('LINUX32'); + {$ENDIF} + {$IFDEF LINUX64} + AddDefine('LINUX64'); + {$ENDIF} + {$IFDEF POSIX} + AddDefine('POSIX'); + {$ENDIF} + {$IFDEF POSIX32} + AddDefine('POSIX32'); + {$ENDIF} + {$IFDEF POSIX64} + AddDefine('POSIX64'); + {$ENDIF} + {$IFDEF CPUARM} + AddDefine('CPUARM'); + {$ENDIF} + {$IFDEF CPUARM32} + AddDefine('CPUARM32'); + {$ENDIF} + {$IFDEF CPUARM64} + AddDefine('CPUARM64'); + {$ENDIF} + {$IFDEF CPU386} + AddDefine('CPU386'); + {$ENDIF} + {$IFDEF CPUX86} + AddDefine('CPUX86'); + {$ENDIF} + {$IFDEF CPUX64} + AddDefine('CPUX64'); + {$ENDIF} + {$IFDEF CPU32BITS} + AddDefine('CPU32BITS'); + {$ENDIF} + {$IFDEF CPU64BITS} + AddDefine('CPU64BITS'); + {$ENDIF} + {$IFDEF MSWINDOWS} + AddDefine('MSWINDOWS'); + {$ENDIF} + {$IFDEF MACOS} + AddDefine('MACOS'); + {$ENDIF} + {$IFDEF MACOS32} + AddDefine('MACOS32'); + {$ENDIF} + {$IFDEF MACOS64} + AddDefine('MACOS64'); + {$ENDIF} + {$IFDEF IOS} + AddDefine('IOS'); + {$ENDIF} + {$IFDEF IOS32} + AddDefine('IOS32'); + {$ENDIF} + {$IFDEF IOS64} + AddDefine('IOS64'); + {$ENDIF} + {$IFDEF ANDROID} + AddDefine('ANDROID'); + {$ENDIF} + {$IFDEF ANDROID32} + AddDefine('ANDROID32'); + {$ENDIF} + {$IFDEF ANDROID64} + AddDefine('ANDROID64'); + {$ENDIF} + {$IFDEF CONSOLE} + AddDefine('CONSOLE'); + {$ENDIF} + {$IFDEF NATIVECODE} + AddDefine('NATIVECODE'); + {$ENDIF} + {$IFDEF CONDITIONALEXPRESSIONS} + AddDefine('CONDITIONALEXPRESSIONS'); + {$ENDIF} + {$IFDEF UNICODE} + AddDefine('UNICODE'); + {$ENDIF} + {$IFDEF ALIGN_STACK} + AddDefine('ALIGN_STACK'); + {$ENDIF} + {$IFDEF ARM_NO_VFP_USE} + AddDefine('ARM_NO_VFP_USE'); + {$ENDIF} + {$IFDEF ASSEMBLER} + AddDefine('ASSEMBLER'); + {$ENDIF} + {$IFDEF AUTOREFCOUNT} + AddDefine('AUTOREFCOUNT'); + {$ENDIF} + {$IFDEF EXTERNALLINKER} + AddDefine('EXTERNALLINKER'); + {$ENDIF} + {$IFDEF ELF} + AddDefine('ELF'); + {$ENDIF} + {$IFDEF NEXTGEN} + AddDefine('NEXTGEN'); + {$ENDIF} + {$IFDEF PC_MAPPED_EXCEPTIONS} + AddDefine('PC_MAPPED_EXCEPTIONS'); + {$ENDIF} + {$IFDEF PIC} + AddDefine('PIC'); + {$ENDIF} + {$IFDEF UNDERSCOREIMPORTNAME} + AddDefine('UNDERSCOREIMPORTNAME'); + {$ENDIF} + {$IFDEF WEAKREF} + AddDefine('WEAKREF'); + {$ENDIF} + {$IFDEF WEAKINSTREF} + AddDefine('WEAKINSTREF'); + {$ENDIF} + {$IFDEF WEAKINTFREF} + AddDefine('WEAKINTFREF'); + {$ENDIF} +end; + +function TmwBasePasLex.GetStringContent: string; +var + TempString: string; + sEnd: Integer; +begin + if TokenID <> ptStringConst then + Result := '' + else + begin + TempString := Token; + sEnd := Length(TempString); + if TempString[sEnd] <> #39 then Inc(sEnd); + Result := Copy(TempString, 2, sEnd - 2); + TempString := ''; + end; +end; + +function TmwBasePasLex.GetIsOrdIdent: Boolean; +begin + if FTokenID = ptIdentifier then + Result := FExID in [ptBoolean, ptByte, ptChar, ptDWord, ptInt64, ptInteger, + ptLongInt, ptLongWord, ptPChar, ptShortInt, ptSmallInt, ptWideChar, ptWord] + else + Result := False; +end; + +function TmwBasePasLex.GetIsOrdinalType: Boolean; +begin + Result := GetIsOrdIdent or (FTokenID in [ptAsciiChar, ptIntegerConst]); +end; + +function TmwBasePasLex.GetIsRealType: Boolean; +begin + if FTokenID = ptIdentifier then + Result := FExID in [ptComp, ptCurrency, ptDouble, ptExtended, ptReal, ptReal48, ptSingle] + else + Result := False; +end; + +function TmwBasePasLex.GetIsStringType: Boolean; +begin + if FTokenID = ptIdentifier then + Result := FExID in [ptAnsiString, ptWideString] + else + Result := FTokenID in [ptString, ptStringConst]; +end; + +function TmwBasePasLex.GetIsVariantType: Boolean; +begin + if FTokenID = ptIdentifier then + Result := FExID in [ptOleVariant, ptVariant] + else + Result := False; +end; + +function TmwBasePasLex.GetOrigin: string; +begin + Result := FBuffer.Buf; +end; + +function TmwBasePasLex.GetIsAddOperator: Boolean; +begin + Result := FTokenID in [ptMinus, ptOr, ptPlus, ptXor]; +end; + +function TmwBasePasLex.GetIsMulOperator: Boolean; +begin + Result := FTokenID in [ptAnd, ptAs, ptDiv, ptMod, ptShl, ptShr, ptSlash, ptStar]; +end; + +function TmwBasePasLex.GetIsRelativeOperator: Boolean; +begin + Result := FTokenID in [ptAs, ptEqual, ptGreater, ptGreaterEqual, ptLower, ptLowerEqual, + ptIn, ptIs, ptNotEqual]; +end; + +function TmwBasePasLex.GetIsCompilerDirective: Boolean; +begin + Result := FTokenID in [ptCompDirect, ptDefineDirect, ptElseDirect, + ptEndIfDirect, ptIfDefDirect, ptIfNDefDirect, ptIfOptDirect, + ptIncludeDirect, ptResourceDirect, ptScopedEnumsDirect, ptUndefDirect]; +end; + +function TmwBasePasLex.GetGenID: TptTokenKind; +begin + Result := FTokenID; + if FTokenID = ptIdentifier then + if FExID <> ptUnknown then Result := FExID; +end; + +{ TmwPasLex } + +constructor TmwPasLex.Create; +begin + inherited Create; + FAheadLex := TmwBasePasLex.Create; +end; + +destructor TmwPasLex.Destroy; +begin + FAheadLex.Free; + inherited Destroy; +end; + +procedure TmwPasLex.AheadNext; +begin + FAheadLex.NextNoJunk; +end; + +function TmwPasLex.GetAheadExID: TptTokenKind; +begin + Result := FAheadLex.ExID; +end; + +function TmwPasLex.GetAheadGenID: TptTokenKind; +begin + Result := FAheadLex.GenID; +end; + +function TmwPasLex.GetAheadToken: string; +begin + Result := FAheadLex.Token; +end; + +function TmwPasLex.GetAheadTokenID: TptTokenKind; +begin + Result := FAheadLex.TokenID; +end; + +procedure TmwPasLex.InitAhead; +begin + FAheadLex.FCommentState := FCommentState; + FAheadLex.CloneDefinesFrom(Self); + + FAheadLex.SetSharedBuffer(FBuffer); + + while FAheadLex.IsJunk do + FAheadLex.Next; +end; + +procedure TmwPasLex.SetOrigin(const NewValue: string); +begin + inherited SetOrigin(NewValue); + FAheadLex.SetSharedBuffer(FBuffer); +end; + +function TmwBasePasLex.Func86: TptTokenKind; +begin + Result := ptIdentifier; + if KeyComp('Varargs') then FExID := ptVarargs; +end; + +procedure TmwBasePasLex.StringDQProc; +begin + if not FAsmCode then + begin + SymbolProc; + Exit; + end; + FTokenID := ptStringDQConst; + repeat + Inc(FBuffer.Run); + case FBuffer.Buf[FBuffer.Run] of + #0, #10, #13: + begin + if Assigned(FOnMessage) then + FOnMessage(Self, meError, 'Unterminated string', PosXY.X, PosXY.Y); + Break; + end; + '\': + begin + Inc(FBuffer.Run); + if CharInSet(FBuffer.Buf[FBuffer.Run], [#32..#127]) then Inc(FBuffer.Run); + end; + end; + until FBuffer.Buf[FBuffer.Run] = '"'; + if FBuffer.Buf[FBuffer.Run] = '"' then + Inc(FBuffer.Run); +end; + +procedure TmwBasePasLex.AmpersandOpProc; +begin + FTokenID := ptAmpersand; + Inc(FBuffer.Run); + while CharInSet(FBuffer.Buf[FBuffer.Run], ['a'..'z', 'A'..'Z','0'..'9', '_', '&']) do + Inc(FBuffer.Run); + FTokenID := ptIdentifier; +end; + +procedure TmwBasePasLex.UpdateScopedEnums; +begin + FScopedEnums := SameText(DirectiveParam, 'ON'); +end; + +initialization + MakeIdentTable; +end. diff --git a/References/DelphiAST/Source/SimpleParser/SimpleParser.Types.pas b/References/DelphiAST/Source/SimpleParser/SimpleParser.Types.pas new file mode 100644 index 000000000..f365251f2 --- /dev/null +++ b/References/DelphiAST/Source/SimpleParser/SimpleParser.Types.pas @@ -0,0 +1,330 @@ +{--------------------------------------------------------------------------- +The contents of this file are subject to the Mozilla Public License Version +1.1 (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at +http://www.mozilla.org/NPL/NPL-1_1Final.html + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for +the specific language governing rights and limitations under the License. + +The Original Code is: mwSimplePasParTypes, released November 14, 1999. + +The Initial Developer of the Original Code is Martin Waldenburg +unit CastaliaPasLexTypes; + +----------------------------------------------------------------------------} + +unit SimpleParser.Types; + +interface + +uses + SysUtils, + TypInfo; + +type + TmwParseError = ( + InvalidAdditiveOperator, + InvalidAccessSpecifier, + InvalidCharString, + InvalidClassMethodHeading, + InvalidConstantDeclaration, + InvalidConstSection, + InvalidDeclarationSection, + InvalidDirective16Bit, + InvalidDirectiveBinding, + InvalidDirectiveCalling, + InvalidExportedHeading, + InvalidForStatement, + InvalidInitializationSection, + InvalidInterfaceDeclaration, + InvalidInterfaceType, + InvalidLabelId, + InvalidLabeledStatement, + InvalidMethodHeading, + InvalidMultiplicativeOperator, + InvalidNumber, + InvalidOrdinalIdentifier, + InvalidParameter, + InvalidParseFile, + InvalidProceduralDirective, + InvalidProceduralType, + InvalidProcedureDeclarationSection, + InvalidProcedureMethodDeclaration, + InvalidRealIdentifier, + InvalidRelativeOperator, + InvalidStorageSpecifier, + InvalidStringIdentifier, + InvalidStructuredType, + InvalidTryStatement, + InvalidTypeKind, + InvalidVariantIdentifier, + InvalidVarSection, + vchInvalidClass, + vchInvalidMethod, + vchInvalidProcedure, + vchInvalidCircuit, + vchInvalidIncludeFile + ); + + TmwPasCodeInfo = ( + ciNone, + ciAccessSpecifier, + ciAdditiveOperator, + ciArrayConstant, + ciArrayType, + ciAsmStatement, + ciBlock, + ciCaseLabel, + ciCaseSelector, + ciCaseStatement, + ciCharString, + ciClassClass, + ciClassField, + ciClassForward, + ciClassFunctionHeading, + ciClassHeritage, + ciClassMemberList, + ciClassMethodDirective, + ciClassMethodHeading, + ciClassMethodOrProperty, + ciClassMethodResolution, + ciClassProcedureHeading, + ciClassProperty, + ciClassReferenceType, + ciClassType, + ciClassTypeEnd, + ciClassVisibility, + ciCompoundStatement, + ciConstantColon, + ciConstantDeclaration, + ciConstantEqual, + ciConstantExpression, + ciConstantName, + ciConstantValue, + ciConstantValueTyped, + ciConstParameter, + ciConstructorHeading, + ciConstructorName, + ciConstSection, + ciContainsClause, + ciContainsExpression, + ciContainsIdentifier, + ciContainsStatement, + ciDeclarationSection, + ciDesignator, + ciDestructorHeading, + ciDestructorName, + ciDirective16Bit, + ciDirectiveBinding, + ciDirectiveCalling, + ciDirectiveDeprecated, + ciDirectiveLibrary, + ciDirectiveLocal, + ciDirectivePlatform, + ciDirectiveVarargs, + ciDispIDSpecifier, + ciDispInterfaceForward, + ciEmptyStatement, + ciEnumeratedType, + ciEnumeratedTypeItem, + ciExceptBlock, + ciExceptionBlockElseBranch, + ciExceptionClassTypeIdentifier, + ciExceptionHandler, + ciExceptionHandlerList, + ciExceptionIdentifier, + ciExceptionVariable, + ciExpliciteType, + ciExportedHeading, + ciExportsClause, + ciExportsElement, + ciExpression, + ciExpressionList, + ciExternalDirective, + ciExternalDirectiveThree, + ciExternalDirectiveTwo, + ciFactor, + ciFieldDeclaration, + ciFieldList, + ciFileType, + ciFormalParameterList, + ciFormalParameterSection, + ciForStatement, + ciForwardDeclaration, + ciFunctionHeading, + ciFunctionMethodDeclaration, + ciFunctionMethodName, + ciFunctionProcedureBlock, + ciFunctionProcedureName, + ciHandlePtCompDirect, + ciHandlePtDefineDirect, + ciHandlePtElseDirect, + ciHandlePtIfDefDirect, + ciHandlePtEndIfDirect, + ciHandlePtIfNDefDirect, + ciHandlePtIfOptDirect, + ciHandlePtIncludeDirect, + ciHandlePtResourceDirect, + ciHandlePtUndefDirect, + ciIdentifier, + ciIdentifierList, + ciIfStatement, + ciImplementationSection, + ciIncludeFile, + ciIndexSpecifier, + ciInheritedStatement, + ciInitializationSection, + ciInlineStatement, + ciInterfaceDeclaration, + ciInterfaceForward, + ciInterfaceGUID, + ciInterfaceHeritage, + ciInterfaceMemberList, + ciInterfaceSection, + ciInterfaceType, + ciLabelDeclarationSection, + ciLabeledStatement, + ciLabelId, + ciLibraryFile, + ciMainUsedUnitExpression, + ciMainUsedUnitName, + ciMainUsedUnitStatement, + ciMainUsesClause, + ciMultiplicativeOperator, + ciNewFormalParameterType, + ciNumber, + ciNextToken, + ciObjectConstructorHeading, + ciObjectDestructorHeading, + ciObjectField, + ciObjectForward, + ciObjectFunctionHeading, + ciObjectHeritage, + ciObjectMemberList, + ciObjectMethodDirective, + ciObjectMethodHeading, + ciObjectNameOfMethod, + ciObjectProcedureHeading, + ciObjectProperty, + ciObjectPropertySpecifiers, + ciObjectType, + ciObjectTypeEnd, + ciObjectVisibility, + ciOldFormalParameterType, + ciOrdinalIdentifier, + ciOrdinalType, + ciOutParameter, + ciPackageFile, + ciParameterFormal, + ciParameterName, + ciParameterNameList, + ciParseFile, + ciPointerType, + ciProceduralDirective, + ciProceduralType, + ciProcedureDeclarationSection, + ciProcedureHeading, + ciProcedureMethodDeclaration, + ciProcedureMethodName, + ciProgramBlock, + ciProgramFile, + ciPropertyDefault, + ciPropertyInterface, + ciPropertyName, + ciPropertyParameterConst, + ciPropertyParameterList, + ciPropertySpecifiers, + ciQualifiedIdentifier, + ciQualifiedIdentifierList, + ciRaiseStatement, + ciReadAccessIdentifier, + ciRealIdentifier, + ciRealType, + ciRecordConstant, + ciRecordFieldConstant, + ciRecordType, + ciRecordVariant, + ciRelativeOperator, + ciRepeatStatement, + ciRequiresClause, + ciRequiresIdentifier, + ciResolutionInterfaceName, + ciResourceDeclaration, + ciReturnType, + ciSEMICOLON, + ciSetConstructor, + ciSetElement, + ciSetType, + ciSimpleExpression, + ciSimpleStatement, + ciSimpleType, + ciSkipAnsiComment, + ciSkipBorComment, + ciSkipSlashesComment, + ciSkipSpace, + ciSkipCRLFco, + ciSkipCRLF, + ciStatement, + ciStatementList, + ciStorageExpression, + ciStorageIdentifier, + ciStorageDefault, + ciStorageNoDefault, + ciStorageSpecifier, + ciStorageStored, + ciStringIdentifier, + ciStringStatement, + ciStringType, + ciStructuredType, + ciSubrangeType, + ciTagField, + ciTagFieldName, + ciTagFieldTypeName, + ciTerm, + ciTryStatement, + ciTypedConstant, + ciTypeDeclaration, + ciTypeId, + ciTypeKind, + ciTypeName, + ciTypeSection, + ciUnitFile, + ciUnitId, + ciUsedUnitName, + ciUsedUnitsList, + ciUsesClause, + ciVarAbsolute, + ciVarEqual, + ciVarDeclaration, + ciVariable, + ciVariableList, + ciVariableReference, + ciVariableTwo, + ciVariantIdentifier, + ciVariantSection, + ciVarParameter, + ciVarSection, + ciVisibilityAutomated, + ciVisibilityPrivate, + ciVisibilityProtected, + ciVisibilityPublic, + ciVisibilityPublished, + ciVisibilityUnknown, + ciWhileStatement, + ciWithStatement, + ciWriteAccessIdentifier + ); + +function ParserErrorName(Value: TmwParseError): string; + +implementation + +function ParserErrorName(Value: TmwParseError): string; +begin + result := GetEnumName(TypeInfo(TmwParseError), Integer(Value)); +end; + +end. + diff --git a/References/DelphiAST/Source/SimpleParser/SimpleParser.inc b/References/DelphiAST/Source/SimpleParser/SimpleParser.inc new file mode 100644 index 000000000..136e6c0a6 --- /dev/null +++ b/References/DelphiAST/Source/SimpleParser/SimpleParser.inc @@ -0,0 +1,332 @@ +{$IFDEF VER370} // Delphi 13 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} + {$DEFINE D21_NEWER} + {$DEFINE D22_NEWER} + {$DEFINE D23_NEWER} + {$DEFINE D24_NEWER} + {$DEFINE D25_NEWER} + {$DEFINE D26_NEWER} + {$DEFINE D27_NEWER} + {$DEFINE D28_NEWER} + {$DEFINE D29_NEWER} + {$DEFINE D37_NEWER} +{$ENDIF} + +{$IFDEF VER360} // Delphi 12 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} + {$DEFINE D21_NEWER} + {$DEFINE D22_NEWER} + {$DEFINE D23_NEWER} + {$DEFINE D24_NEWER} + {$DEFINE D25_NEWER} + {$DEFINE D26_NEWER} + {$DEFINE D27_NEWER} + {$DEFINE D28_NEWER} + {$DEFINE D29_NEWER} +{$ENDIF} + +{$IFDEF VER350} // Delphi 11 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} + {$DEFINE D21_NEWER} + {$DEFINE D22_NEWER} + {$DEFINE D23_NEWER} + {$DEFINE D24_NEWER} + {$DEFINE D25_NEWER} + {$DEFINE D26_NEWER} + {$DEFINE D27_NEWER} + {$DEFINE D28_NEWER} +{$ENDIF} + +{$IFDEF VER340} // Delphi 10.4 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} + {$DEFINE D21_NEWER} + {$DEFINE D22_NEWER} + {$DEFINE D23_NEWER} + {$DEFINE D24_NEWER} + {$DEFINE D25_NEWER} + {$DEFINE D26_NEWER} + {$DEFINE D27_NEWER} +{$ENDIF} + +{$IFDEF VER330} // Delphi 10.3 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} + {$DEFINE D21_NEWER} + {$DEFINE D22_NEWER} + {$DEFINE D23_NEWER} + {$DEFINE D24_NEWER} + {$DEFINE D25_NEWER} + {$DEFINE D26_NEWER} +{$ENDIF} + +{$IFDEF VER320} // Delphi 10 Tokyo + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} + {$DEFINE D21_NEWER} + {$DEFINE D22_NEWER} + {$DEFINE D23_NEWER} + {$DEFINE D24_NEWER} + {$DEFINE D25_NEWER} +{$ENDIF} + +{$IFDEF VER310} // Delphi 10 Berlin + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} + {$DEFINE D21_NEWER} + {$DEFINE D22_NEWER} + {$DEFINE D23_NEWER} + {$DEFINE D24_NEWER} +{$ENDIF} + +{$IFDEF VER300} // Delphi 10 Seattle + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} + {$DEFINE D21_NEWER} + {$DEFINE D22_NEWER} + {$DEFINE D23_NEWER} +{$ENDIF} + +{$IFDEF VER290} // Delphi XE8 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} + {$DEFINE D21_NEWER} + {$DEFINE D22_NEWER} +{$ENDIF} + +{$IFDEF VER280} // Delphi XE7 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} + {$DEFINE D21_NEWER} +{$ENDIF} + +{$IFDEF VER270} // Delphi XE6 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} + {$DEFINE D20_NEWER} +{$ENDIF} + +{$IFDEF VER260} // Delphi XE5 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} + {$DEFINE D19_NEWER} +{$ENDIF} + +{$IFDEF VER250} // Delphi XE4 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} + {$DEFINE D18_NEWER} +{$ENDIF} + +{$IFDEF VER240} // Delphi XE3 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} + {$DEFINE D17_NEWER} +{$ENDIF} + +{$IFDEF VER230} // Delphi XE2 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} + {$DEFINE D16_NEWER} +{$ENDIF} + +{$IFDEF VER220} // Delphi XE + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} + {$DEFINE D15_NEWER} +{$ENDIF} + +{$IFDEF VER210} // Delphi 2010 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} + {$DEFINE D14_NEWER} +{$ENDIF} + +{$IFDEF VER200} // Delphi 2009 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} + {$DEFINE D12_NEWER} +{$ENDIF} + +{$IFDEF VER190} // Delphi 2007 .NET + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} + {$DEFINE D11_NEWER} +{$ENDIF} + +{$IFDEF VER185} // Delphi 2007 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} +{$ENDIF} + +{$IFDEF VER180} // Delphi 2006 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} + {$DEFINE D10_NEWER} +{$ENDIF} + +{$IFDEF VER170} // Delphi 2005 + {$DEFINE D8_NEWER} + {$DEFINE D9_NEWER} +{$ENDIF} + +{$IFDEF VER160} // Delphi 8 + {$DEFINE D8_NEWER} +{$ENDIF} + +{$IFDEF D18_NEWER} + {$DEFINE SUPPORTS_INTRINSIC_HELPERS} +{$ENDIF} + +{$IFNDEF D16_NEWER} + {$DEFINE CPUX86} +{$ENDIF} diff --git a/References/DelphiAST/Source/SimpleParser/SimpleParser.pas b/References/DelphiAST/Source/SimpleParser/SimpleParser.pas new file mode 100644 index 000000000..b9a4c6ae5 --- /dev/null +++ b/References/DelphiAST/Source/SimpleParser/SimpleParser.pas @@ -0,0 +1,5931 @@ +{--------------------------------------------------------------------------- +The contents of this file are subject to the Mozilla Public License Version +1.1 (the "License"); you may not use this file except in compliance with the +License. You may obtain a copy of the License at +http://www.mozilla.org/NPL/NPL-1_1Final.html + +Software distributed under the License is distributed on an "AS IS" basis, +WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for +the specific language governing rights and limitations under the License. + +The Original Code is: mwSimplePasPar.pas, released November 14, 1999. + +The Initial Developer of the Original Code is Martin Waldenburg +(Martin.Waldenburg@T-Online.de). +Portions created by Martin Waldenburg are Copyright (C) 1998, 1999 Martin +Waldenburg. +All Rights Reserved. +Portions CopyRight by Robert Zierer. + +Contributor(s): Vladimir Churbanov, Dean Hill, James Jacobson, LaKraven Studios Ltd, Roman Yankovsky +(This list is ALPHABETICAL) + +Last Modified: 2014/09/14 +Current Version: 1.10 + +Notes: This program is an early beginning of a Pascal parser. +I'd like to invite the Delphi community to develop it further and to create +a fully featured Object Pascal parser. + +Modification history: + +LaKraven Studios Ltd, January 2015: + +- Cleaned up version-specifics up to XE8 +- Fixed all warnings & hints + +Jacob Thurman between 20040301 and 20020401 + +Made ready for Delphi 8: + +Added new directives and keywords: static, sealed, final, operator, unsafe. + +Added parsing for custom attributes (based on ECMA C# specification). + +Added support for nested types in class declarations. + +Jeff Rafter between 20020116 and 20020302 + +Added AncestorId and AncestorIdList back in, but now treat them as Qualified +Identifiers per Daniel Rolf's fix. The separation from QualifiedIdentifierList +is need for descendent classes. + +Added VarName and VarNameList back in for descendent classes, fixed to correctly +use Identifiers as in Daniel's verison + +Removed fInJunk flags (they were never used, only set) + +Pruned uses clause to remove windows dependency. This required changing +"TPoint" to "TTokenPoint". TTokenPoint was declared in mwPasLexTypes + +Daniel Rolf between 20010723 and 20020116 + +Made ready for Delphi 6 + +ciClassClass for "class function" etc. +ciClassTypeEnd marks end of a class declaration (I needed that for the delphi-objectif-connector) +ciEnumeratedTypeItem for items of enumerations +ciDirectiveXXX for the platform, deprecated, varargs, local +ciForwardDeclaration for "forward" (until now it has been read but no event) +ciIndexSpecifier for properties +ciObjectTypeEnd marks end of an object declaration +ciObjectProperty property for objects +ciObjectPropertySpecifiers property for objects +ciPropertyDefault marking default of property +ciDispIDSpecifier for dispid + +patched some functions for implementing the above things and patching the following bugs/improv.: + +ObjectProperty handling overriden properties +ProgramFile, UnitFile getting Identifier instead of dropping it +InterfaceHeritage: Qualified identifiers +bugs in variant records +typedconstant failed with complex set constants. simple patch using ConstantExpression + +German localization for the two string constants. Define GERMAN for german string constants. + +Greg Chapman on 20010522 +Better handling of defaut array property +Separate handling of X and Y in property Pixels[X, Y: Integer through identifier "event" +corrected spelling of "ForwardDeclaration" + +James Jacobson on 20010223 +semi colon before finalization fix + +James Jacobson on 20010223 +RecordConstant Fix + +Martin waldenburg on 2000107 +Even Faster lexer implementation !!!! + +James Jacobson on 20010107 + Improper handling of the construct + property TheName: Integer read FTheRecord.One.Two; (stop at second point) + where one and two are "qualifiable" structures. + +James Jacobson on 20001221 + Stops at the second const. + property Anchor[const Section: string; const Ident:string]: string read + changed TmwSimplePasPar.PropertyParameterList + +On behalf of Martin Waldenburg and James Jacobson + Correction in array property Handling (Matin and James) 07/12/2000 + Use of ExId instead of TokenId in ExportsElements (James) 07/12/2000 + Reverting to old behavior in Statementlist [PtintegerConst put back in] (James) 07/12/2000 + +Xavier Masson InnerCircleProject : XM : 08/11/2000 + Integration of the new version delivered by Martin Waldenburg with the modification I made described just below + +Xavier Masson InnerCircleProject : XM : 07/15/2000 + Added "states/events " for spaces( SkipSpace;) CRLFco (SkipCRLFco) and + CRLF (SkipCRLF) this way the parser can give a complete view on code allowing + "perfect" code reconstruction. + (I fully now that this is not what a standard parser will do but I think it is more usefull this way ;) ) + go to www.innercircleproject.com for more explanations or express your critisism ;) + +previous modifications not logged sorry ;) + +Known Issues: +-----------------------------------------------------------------------------} +{---------------------------------------------------------------------------- + Last Modified: 05/22/2001 + Current Version: 1.1 + official version + Maintained by InnerCircle + + http://www.innercircleproject.org + + 02/07/2001 + added property handling in Object types + changed handling of forward declarations in ExportedHeading method +-----------------------------------------------------------------------------} +unit SimpleParser; + +{$IFDEF FPC}{$MODE DELPHI}{$ENDIF} + +interface + +uses + SysUtils, + Classes, + SimpleParser.Lexer.Types, + SimpleParser.Lexer, + SimpleParser.Types; + +{$INCLUDE SimpleParser.inc} + +resourcestring + rsExpected = '''%s'' expected found ''%s'''; + rsEndOfFile = 'end of file'; + +const + ClassMethodDirectiveEnum = [ + ptAbstract, + ptCdecl, + ptDynamic, + ptMessage, + ptOverride, + ptOverload, + ptPascal, + ptRegister, + ptReintroduce, + ptSafeCall, + ptStdCall, + ptVirtual, + ptDeprecated, + ptLibrary, + ptPlatform, + ptStatic, + ptInline, + ptFinal, + ptExperimental, + ptDispId, + ptNoreturn + ]; + +type + ESyntaxError = class(Exception) + private + FPosXY: TTokenPoint; + public + constructor Create(const Msg: string); + constructor CreateFmt(const Msg: string; const Args: array of const); + constructor CreatePos(const Msg: string; aPosXY: TTokenPoint); + property PosXY: TTokenPoint read FPosXY write FPosXY; + end; + + TmwSimplePasPar = class(TObject) + private + FOnMessage: TMessageEvent; + FLexer: TmwPasLex; + FInterfaceOnly: Boolean; + FLastNoJunkPos: Integer; + FLastNoJunkLen: Integer; + AheadParse: TmwSimplePasPar; + FInRound: Integer; + procedure InitAhead; + procedure VariableTail; + function GetInRound: Boolean; + function GetUseDefines: Boolean; + function GetScopedEnums: Boolean; + procedure SetUseDefines(const Value: Boolean); + procedure SetIncludeHandler(IncludeHandler: IIncludeHandler); + function GetOnComment: TCommentEvent; + procedure SetOnComment(const Value: TCommentEvent); + protected + procedure Expected(Sym: TptTokenKind); virtual; + procedure ExpectedEx(Sym: TptTokenKind); virtual; + procedure ExpectedFatal(Sym: TptTokenKind); virtual; + procedure HandlePtCompDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtDefineDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtElseDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtEndIfDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtIfDefDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtIfNDefDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtIfOptDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtResourceDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtUndefDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtIfDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtIfEndDirect(Sender: TmwBasePasLex); virtual; + procedure HandlePtElseIfDirect(Sender: TmwBasePasLex); virtual; + procedure NextToken; virtual; + procedure SkipJunk; virtual; + procedure Semicolon; virtual; + function GetExID: TptTokenKind; virtual; + function GetTokenID: TptTokenKind; virtual; + function GetGenID: TptTokenKind; virtual; + procedure AccessSpecifier; virtual; + procedure AdditiveOperator; virtual; + procedure AddressOp; virtual; + procedure AlignmentParameter; virtual; + procedure AsOp; virtual; + procedure AncestorIdList; virtual; + procedure AncestorId; virtual; + procedure AnonymousMethod; virtual; + procedure AnonymousMethodType; virtual; + procedure ArrayConstant; virtual; + procedure ArrayBounds; virtual; + procedure ArrayDimension; virtual; + procedure ArrayType; virtual; + procedure AsmStatement; virtual; + procedure AssignOp; virtual; + procedure AtExpression; virtual; + procedure Block; virtual; + procedure CaseElseStatement; virtual; + procedure CaseLabel; virtual; + procedure CaseLabelList; virtual; + procedure CaseSelector; virtual; + procedure CaseStatement; virtual; + procedure CharString; virtual; + procedure ClassField; virtual; + procedure ClassForward; virtual; + procedure ClassFunctionHeading; virtual; + procedure ClassHelper; virtual; + procedure ClassHeritage; virtual; + procedure ClassMemberList; virtual; + procedure ClassMethodDirective; virtual; + procedure ClassMethodHeading; virtual; + procedure ClassMethodOrProperty; virtual; + procedure ClassMethodResolution; virtual; + procedure ClassOperatorHeading; virtual; + procedure ClassProcedureHeading; virtual; + procedure ClassClass; virtual; + procedure ClassConstraint; virtual; + procedure ClassMethod; virtual; + procedure ClassProperty; virtual; + procedure ClassReferenceType; virtual; + procedure ClassType; virtual; + procedure ClassTypeEnd; virtual; + procedure ClassVisibility; virtual; + procedure CompoundStatement; virtual; + procedure ConstantColon; virtual; + procedure ConstantDeclaration; virtual; + procedure ConstantEqual; virtual; + procedure ConstantExpression; virtual; + procedure ConstantName; virtual; + procedure ConstantType; virtual; + procedure ConstantValue; virtual; + procedure ConstantValueTyped; virtual; + procedure ConstParameter; virtual; + procedure ConstructorConstraint; virtual; + procedure ConstructorHeading; virtual; + procedure ConstructorName; virtual; + procedure ConstSection; virtual; + procedure ContainsClause; virtual; + procedure CustomAttribute; virtual; + procedure DeclarationSection; virtual; + procedure DeclarationSections; virtual; + procedure Designator; virtual; + procedure DestructorHeading; virtual; + procedure DestructorName; virtual; + procedure Directive16Bit; virtual; + procedure DirectiveBinding; virtual; + procedure DirectiveBindingMessage; virtual; + procedure DirectiveCalling; virtual; + procedure DirectiveDeprecated; virtual; + procedure DirectiveInline; virtual; + procedure DirectiveLibrary; virtual; + procedure DirectiveLocal; virtual; + procedure DirectivePlatform; virtual; + procedure DirectiveVarargs; virtual; + procedure DispInterfaceForward; virtual; + procedure DispIDSpecifier; virtual; + procedure DotOp; virtual; + procedure ElseStatement; virtual; + procedure ElseExpression; virtual; + procedure EmptyStatement; virtual; + procedure EnumeratedType; virtual; + procedure EnumeratedTypeItem; virtual; + procedure ExceptBlock; virtual; + procedure ExceptionBlockElseBranch; virtual; + procedure ExceptionClassTypeIdentifier; virtual; + procedure ExceptionHandler; virtual; + procedure ExceptionHandlerList; virtual; + procedure ExceptionIdentifier; virtual; + procedure ExceptionVariable; virtual; + procedure ExplicitType; virtual; + procedure ExportedHeading; virtual; + procedure ExportsClause; virtual; + procedure ExportsElement; virtual; + procedure ExportsName; virtual; + procedure ExportsNameId; virtual; + procedure Expression; virtual; + procedure ExpressionList; virtual; + procedure ExternalDirective; virtual; + procedure ExternalDirectiveThree; virtual; + procedure ExternalDirectiveTwo; virtual; + procedure Factor; virtual; + procedure FieldDeclaration; virtual; + procedure FieldList; virtual; + procedure FieldNameList; virtual; + procedure FieldName; virtual; + procedure FileType; virtual; + procedure FinalizationSection; virtual; + procedure FinallyBlock; virtual; + procedure FormalParameterList; virtual; + procedure FormalParameterSection; virtual; + procedure ForStatement; virtual; + procedure ForStatementDownTo; virtual; + procedure ForStatementFrom; virtual; + procedure ForStatementIn; virtual; + procedure ForStatementTo; virtual; + procedure ForwardDeclaration; virtual; + procedure FunctionHeading; virtual; + procedure FunctionMethodDeclaration; virtual; + procedure FunctionMethodName; virtual; + procedure FunctionProcedureBlock; virtual; + procedure FunctionProcedureName; virtual; + procedure GotoStatement; virtual; + procedure Identifier; virtual; + procedure IdentifierList; virtual; + procedure IfStatement; virtual; + procedure TernaryOp; virtual; + procedure ImplementationSection; virtual; + procedure ImplementsSpecifier; virtual; + procedure IncludeFile; virtual; + procedure IndexSpecifier; virtual; + procedure IndexOp; virtual; + procedure InheritedStatement; virtual; + procedure InheritedVariableReference; virtual; + procedure InitializationSection; virtual; + procedure InlineConstSection; virtual; + procedure InlineStatement; virtual; + procedure InlineVarDeclaration; virtual; + procedure InlineVarSection; virtual; + procedure InParameter; virtual; + procedure InterfaceDeclaration; virtual; + procedure InterfaceForward; virtual; + procedure InterfaceGUID; virtual; + procedure InterfaceHeritage; virtual; + procedure InterfaceMemberList; virtual; + procedure InterfaceSection; virtual; + procedure InterfaceType; virtual; + procedure IsNotOp; virtual; + procedure LabelDeclarationSection; virtual; + procedure LabeledStatement; virtual; + procedure LabelId; virtual; + procedure LibraryFile; virtual; + procedure LibraryBlock; virtual; + procedure MainUsedUnitExpression; virtual; + procedure MainUsedUnitName; virtual; + procedure MainUsedUnitStatement; virtual; + procedure MainUsesClause; virtual; + procedure MethodKind; virtual; + procedure MultiplicativeOperator; virtual; + procedure FormalParameterType; virtual; + procedure NotInOp; virtual; + procedure NotOp; virtual; + procedure NilToken; virtual; + procedure Number; virtual; + procedure ObjectConstructorHeading; virtual; + procedure ObjectDestructorHeading; virtual; + procedure ObjectField; virtual; + procedure ObjectForward; virtual; + procedure ObjectFunctionHeading; virtual; + procedure ObjectHeritage; virtual; + procedure ObjectMemberList; virtual; + procedure ObjectMethodDirective; virtual; + procedure ObjectMethodHeading; virtual; + procedure ObjectNameOfMethod; virtual; + procedure ObjectProperty; virtual; + procedure ObjectPropertySpecifiers; virtual; + procedure ObjectProcedureHeading; virtual; + procedure ObjectType; virtual; + procedure ObjectTypeEnd; virtual; + procedure ObjectVisibility; virtual; + procedure OrdinalIdentifier; virtual; + procedure OrdinalType; virtual; + procedure OutParameter; virtual; + procedure PackageFile; virtual; + procedure ParameterFormal; virtual; + procedure ParameterName; virtual; + procedure ParameterNameList; virtual; + procedure ParseFile; virtual; + procedure PointerSymbol; virtual; + procedure PointerType; virtual; + procedure ProceduralDirective; virtual; + procedure ProceduralDirectiveList; virtual; + procedure ProceduralDirectiveOf; virtual; + procedure ProceduralType; virtual; + procedure ProcedureDeclarationSection; virtual; + procedure ProcedureHeading; virtual; + procedure ProcedureProcedureName; virtual; + procedure ProcedureMethodName; virtual; + procedure ProgramBlock; virtual; + procedure ProgramFile; virtual; + procedure PropertyDefault; virtual; + procedure PropertyInterface; virtual; + procedure PropertyName; virtual; + procedure PropertyParameterList; virtual; + procedure PropertySpecifiers; virtual; + procedure QualifiedIdentifier; virtual; + procedure RaiseStatement; virtual; + procedure ReadAccessIdentifier; virtual; + procedure RealIdentifier; virtual; + procedure RealType; virtual; + procedure RecordAlign; virtual; + procedure RecordAlignValue; virtual; + procedure RecordConstant; virtual; + procedure RecordConstraint; virtual; + procedure RecordFieldConstant; virtual; + procedure RecordType; virtual; + procedure RecordVariant; virtual; + procedure RelativeOperator; virtual; + procedure RepeatStatement; virtual; + procedure RequiresClause; virtual; + procedure RequiresIdentifier; virtual; + procedure RequiresIdentifierId; virtual; + procedure ResolutionInterfaceName; virtual; + procedure ResourceDeclaration; virtual; + procedure ResourceValue; virtual; + procedure ReturnType; virtual; + procedure RoundClose; virtual; + procedure RoundOpen; virtual; + procedure SetConstructor; virtual; + procedure SetElement; virtual; + procedure SetType; virtual; + procedure SimpleExpression; virtual; + procedure SimpleStatement; virtual; + procedure SimpleType; virtual; + procedure SkipAnsiComment; virtual; + procedure SkipBorComment; virtual; + procedure SkipSlashesComment; virtual; + procedure SkipSpace; virtual; + procedure SkipCRLFco; virtual; + procedure SkipCRLF; virtual; + procedure Statement; virtual; + procedure StatementOrExpression; virtual; + procedure Statements; virtual; + procedure StatementList; virtual; + procedure StorageExpression; virtual; + procedure StorageIdentifier; virtual; + procedure StorageDefault; virtual; + procedure StorageNoDefault; virtual; + procedure StorageSpecifier; virtual; + procedure StorageStored; virtual; + procedure StringConst; virtual; + procedure StringConstSimple; virtual; + procedure StringIdentifier; virtual; + procedure StringStatement; virtual; + procedure StringType; virtual; + procedure StructuredType; virtual; + procedure SubrangeType; virtual; + procedure TagField; virtual; + procedure TagFieldName; virtual; + procedure TagFieldTypeName; virtual; + procedure Term; virtual; + procedure ThenStatement; virtual; + procedure ThenExpression; virtual; + procedure TryStatement; virtual; + procedure TypedConstant; virtual; + procedure TypeDeclaration; virtual; + procedure TypeId; virtual; + procedure TypeKind; virtual; + procedure TypeName; virtual; + procedure TypeReferenceType; virtual; + procedure TypeSimple; virtual; + //generics + procedure TypeArgs; virtual; + procedure TypeDirective; virtual; + procedure TypeParams; virtual; + procedure TypeParamDecl; virtual; + procedure TypeParamDeclList; virtual; + procedure TypeParamList; virtual; + procedure ConstraintList; virtual; + procedure Constraint; virtual; + //end generics + procedure TypeSection; virtual; + procedure UnaryMinus; virtual; + procedure UnitFile; virtual; + procedure UnitId; virtual; + procedure UnitName; virtual; + procedure UsedUnitName; virtual; + procedure UsedUnitsList; virtual; + procedure UsesClause; virtual; + procedure VarAbsolute; virtual; + procedure VarEqual; virtual; + procedure VarDeclaration; virtual; + procedure Variable; virtual; + procedure VariableReference; virtual; + procedure VariantIdentifier; virtual; + procedure VariantSection; virtual; + procedure VarParameter; virtual; + procedure VarName; virtual; + procedure VarNameList; virtual; + procedure VarSection; virtual; + procedure VisibilityAutomated; virtual; + procedure VisibilityPrivate; virtual; + procedure VisibilityProtected; virtual; + procedure VisibilityPublic; virtual; + procedure VisibilityPublished; virtual; + procedure VisibilityStrictPrivate; virtual; + procedure VisibilityStrictProtected; virtual; + procedure VisibilityUnknown; virtual; + procedure WhileStatement; virtual; + procedure WithExpressionList; virtual; + procedure WithStatement; virtual; + procedure WriteAccessIdentifier; virtual; + //JThurman 2004-03-21 + {This is the syntax for custom attributes, based quite strictly on the + ECMA syntax specifications for C#, but with a Delphi expression being + used at the bottom as opposed to a C# expression} + procedure GlobalAttributes; + procedure GlobalAttributeSections; + procedure GlobalAttributeSection; + procedure GlobalAttributeTargetSpecifier; + procedure GlobalAttributeTarget; + procedure Attributes; + procedure AttributeSections; virtual; + procedure AttributeSection; + procedure AttributeTargetSpecifier; + procedure AttributeTarget; + procedure AttributeList; + procedure Attribute; virtual; + procedure AttributeName; virtual; + procedure AttributeArguments; virtual; + procedure PositionalArgumentList; + procedure PositionalArgument; virtual; + procedure NamedArgumentList; + procedure NamedArgument; virtual; + procedure AttributeArgumentName; virtual; + procedure AttributeArgumentExpression; virtual; + + property ExID: TptTokenKind read GetExID; + property GenID: TptTokenKind read GetGenID; + property TokenID: TptTokenKind read GetTokenID; + property InRound: Boolean read GetInRound; + public + constructor Create; virtual; + destructor Destroy; override; + procedure SynError(Error: TmwParseError); virtual; + procedure Run(const UnitName: string; SourceStream: TStream); virtual; + + procedure ClearDefines; + procedure InitDefinesDefinedByCompiler; + procedure AddDefine(const ADefine: string); + procedure RemoveDefine(const ADefine: string); + function IsDefined(const ADefine: string): Boolean; + + property InterfaceOnly: Boolean read FInterfaceOnly write FInterfaceOnly; + property Lexer: TmwPasLex read FLexer; + property OnComment: TCommentEvent read GetOnComment write SetOnComment; + property OnMessage: TMessageEvent read FOnMessage write FOnMessage; + property LastNoJunkPos: Integer read FLastNoJunkPos; + property LastNoJunkLen: Integer read FLastNoJunkLen; + + property UseDefines: Boolean read GetUseDefines write SetUseDefines; + property ScopedEnums: Boolean read GetScopedEnums; + property IncludeHandler: IIncludeHandler write SetIncludeHandler; + end; + +implementation + +{ ESyntaxError } + +constructor ESyntaxError.Create(const Msg: string); +begin + FPosXY.X := -1; + FPosXY.Y := -1; + inherited Create(Msg); +end; + +constructor ESyntaxError.CreateFmt(const Msg: string; const Args: array of const); +begin + FPosXY.X := -1; + FPosXY.Y := -1; + inherited CreateFmt(Msg, Args); +end; + +constructor ESyntaxError.CreatePos(const Msg: string; aPosXY: TTokenPoint); +begin + FPosXY := aPosXY; + inherited Create(Msg); +end; + +{ TmwSimplePasPar } + +procedure TmwSimplePasPar.ForwardDeclaration; +begin + NextToken; + Semicolon; +end; + +procedure TmwSimplePasPar.ObjectProperty; +begin + Expected(ptProperty); + PropertyName; + case TokenID of + ptColon, ptSquareOpen: + begin + PropertyInterface; + end; + end; + ObjectPropertySpecifiers; + case ExID of + ptDefault: + begin + PropertyDefault; + Semicolon; + end; + end; +end; + +procedure TmwSimplePasPar.ObjectPropertySpecifiers; +begin + if ExID = ptIndex then + begin + IndexSpecifier; + end; + while ExID in [ptRead, ptReadOnly, ptWrite, ptWriteOnly] do + begin + AccessSpecifier; + end; + while ExID in [ptDefault, ptNoDefault, ptStored] do + begin + StorageSpecifier; + end; + Semicolon; +end; + +type + TStringStreamHelper = class helper for TStringStream + function GetDataString: string; + {$IFNDEF FPC} + property DataString: string read GetDataString; + {$ENDIF} + end; + +function TStringStreamHelper.GetDataString: string; +{$IFNDEF FPC} +var + Encoding: TEncoding; +begin + // try to read a bom from the buffer to create the correct encoding + // but only if the encoding is still the default encoding + if Self.Encoding = TEncoding.Default then + begin + Encoding := nil; + TEncoding.GetBufferEncoding(Bytes, Encoding); + Result := Encoding.GetString(Bytes, Length(Encoding.GetPreamble), Size); + end + else + Result := Self.Encoding.GetString(Bytes, 0, Size); +{$ELSE} +var + Encoding: TEncoding; + Bytes: TBytes; +begin + Encoding := nil; + SetLength(Bytes, Self.Size); + Bytes := BytesOf(DataString); + TEncoding.GetBufferEncoding(Bytes, Encoding); + Result := Encoding.GetString(Bytes, Length(Encoding.GetPreamble), Size); +{$ENDIF} +end; + +procedure TmwSimplePasPar.Run(const UnitName: string; SourceStream: TStream); +var + StringStream: TStringStream; + OwnStream: Boolean; +{$IFDEF FPC} + Strings: TStringList; +{$ENDIF} +begin + OwnStream := not (SourceStream is TStringStream); + if OwnStream then + begin + {$IFNDEF FPC} + StringStream := TStringStream.Create; + StringStream.LoadFromStream(SourceStream); + {$ELSE} + Strings := TStringList.Create; + try + Strings.LoadFromStream(SourceStream); + StringStream := TStringStream.Create(''); + Strings.SaveToStream(StringStream); + finally + FreeAndNil(Strings); + end; + {$ENDIF} + end + else + StringStream := TStringStream(SourceStream); + FLexer.Origin := StringStream.GetDataString; + ParseFile; + if OwnStream then + StringStream.Free; +end; + +constructor TmwSimplePasPar.Create; +begin + inherited Create; + FLexer := TmwPasLex.Create; + FLexer.OnCompDirect := HandlePtCompDirect; + FLexer.OnDefineDirect := HandlePtDefineDirect; + FLexer.OnElseDirect := HandlePtElseDirect; + FLexer.OnEndIfDirect := HandlePtEndIfDirect; + FLexer.OnIfDefDirect := HandlePtIfDefDirect; + FLexer.OnIfNDefDirect := HandlePtIfNDefDirect; + FLexer.OnIfOptDirect := HandlePtIfOptDirect; + FLexer.OnResourceDirect := HandlePtResourceDirect; + FLexer.OnUnDefDirect := HandlePtUndefDirect; + FLexer.OnIfDirect := HandlePtIfDirect; + FLexer.OnIfEndDirect := HandlePtIfEndDirect; + FLexer.OnElseIfDirect := HandlePtElseIfDirect; +end; + +destructor TmwSimplePasPar.Destroy; +begin + AheadParse.Free; + + FLexer.Free; + inherited Destroy; +end; + +{next two check for ptNull and ExpectedFatal for an EOF Error} + +procedure TmwSimplePasPar.Expected(Sym: TptTokenKind); +begin + if Sym <> Lexer.TokenID then + begin + if TokenID = ptNull then + ExpectedFatal(Sym) + else + begin + if Assigned(FOnMessage) then + FOnMessage(Self, meError, Format(rsExpected, [TokenName(Sym), FLexer.Token]), + FLexer.PosXY.X, FLexer.PosXY.Y); + end; + end + else + NextToken; +end; + +procedure TmwSimplePasPar.ExpectedEx(Sym: TptTokenKind); +begin + if Sym <> Lexer.ExID then + begin + if Lexer.TokenID = ptNull then + ExpectedFatal(Sym) {jdj 7/22/1999} + else if Assigned(FOnMessage) then + FOnMessage(Self, meError, Format(rsExpected, ['EX:' + TokenName(Sym), FLexer.Token]), + FLexer.PosXY.X, FLexer.PosXY.Y); + end + else + NextToken; +end; + +{Replace Token with cnEndOfFile if TokenId = ptnull} + +procedure TmwSimplePasPar.ExpectedFatal(Sym: TptTokenKind); +var + tS: string; +begin + if Sym <> Lexer.TokenID then + begin + {--jdj 7/22/1999--} + if Lexer.TokenId = ptNull then + tS := rsEndOfFile + else + tS := FLexer.Token; + {--jdj 7/22/1999--} + raise ESyntaxError.CreatePos(Format(rsExpected, [TokenName(Sym), tS]), FLexer.PosXY); + end + else + NextToken; +end; + +procedure TmwSimplePasPar.HandlePtCompDirect(Sender: TmwBasePasLex); +begin + if Assigned(FOnMessage) then + FOnMessage(Self, meNotSupported, 'Currently not supported ' + FLexer.Token, FLexer.PosXY.X, FLexer.PosXY.Y); + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtDefineDirect(Sender: TmwBasePasLex); +begin + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtElseDirect(Sender: TmwBasePasLex); +begin + if Sender = Lexer then + NextToken + else + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtElseIfDirect(Sender: TmwBasePasLex); +begin + if Sender = Lexer then + NextToken + else + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtEndIfDirect(Sender: TmwBasePasLex); +begin + if Sender = Lexer then + NextToken + else + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtIfDefDirect(Sender: TmwBasePasLex); +begin + if Sender = Lexer then + NextToken + else + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtIfDirect(Sender: TmwBasePasLex); +begin + if Sender = Lexer then + NextToken + else + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtIfEndDirect(Sender: TmwBasePasLex); +begin + if Sender = Lexer then + NextToken + else + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtIfNDefDirect(Sender: TmwBasePasLex); +begin + if Sender = Lexer then + NextToken + else + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtIfOptDirect(Sender: TmwBasePasLex); +begin + if Assigned(FOnMessage) then + FOnMessage(Self, meNotSupported, 'Currently not supported ' + FLexer.Token, FLexer.PosXY.X, FLexer.PosXY.Y); + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtResourceDirect(Sender: TmwBasePasLex); +begin + if Assigned(FOnMessage) then + FOnMessage(Self, meNotSupported, 'Currently not supported ' + FLexer.Token, FLexer.PosXY.X, FLexer.PosXY.Y); + Sender.Next; +end; + +procedure TmwSimplePasPar.HandlePtUndefDirect(Sender: TmwBasePasLex); +begin + Sender.Next; +end; + +procedure TmwSimplePasPar.NextToken; +begin + FLexer.NextNoJunk; +end; + +procedure TmwSimplePasPar.NilToken; +begin + Expected(ptNil); +end; + +procedure TmwSimplePasPar.NotInOp; +begin + Expected(ptNot); + Expected(ptIn); +end; + +procedure TmwSimplePasPar.NotOp; +begin + Expected(ptNot); +end; + +procedure TmwSimplePasPar.SkipJunk; +begin + if Lexer.IsJunk then + begin + case TokenID of + ptAnsiComment: + begin + SkipAnsiComment; + end; + ptBorComment: + begin + SkipBorComment; + end; + ptSlashesComment: + begin + SkipSlashesComment; + end; + ptSpace: + begin + SkipSpace; + end; + ptCRLFCo: + begin + SkipCRLFco; + end; + ptCRLF: + begin + SkipCRLF; + end; + ptSquareOpen: + begin + CustomAttribute; + end; + else + begin + Lexer.Next; + end; + end; + end; + FLastNoJunkPos := Lexer.TokenPos; + FLastNoJunkLen := Lexer.TokenLen; +end; + +procedure TmwSimplePasPar.SkipAnsiComment; +begin + Expected(ptAnsiComment); + while TokenID in [ptAnsiComment] do + Lexer.Next; +end; + +procedure TmwSimplePasPar.SkipBorComment; +begin + Expected(ptBorComment); + while TokenID in [ptBorComment] do + Lexer.Next; +end; + +procedure TmwSimplePasPar.SkipSlashesComment; +begin + Expected(ptSlashesComment); +end; + +procedure TmwSimplePasPar.ThenExpression; +begin + Expected(ptThen); + Expression; +end; + +procedure TmwSimplePasPar.ThenStatement; +begin + Expected(ptThen); + Statement; +end; + +procedure TmwSimplePasPar.Semicolon; +begin + case Lexer.TokenID of + ptElse, ptEnd, ptExcept, ptfinally, ptFinalization, ptRoundClose, ptUntil: ; + else + Expected(ptSemiColon); + end; +end; + +function TmwSimplePasPar.GetExID: TptTokenKind; +begin + Result := FLexer.ExID; +end; + +function TmwSimplePasPar.GetTokenID: TptTokenKind; +begin + Result := FLexer.TokenID; +end; + +function TmwSimplePasPar.GetUseDefines: Boolean; +begin + Result := FLexer.UseDefines; +end; + +function TmwSimplePasPar.GetScopedEnums: Boolean; +begin + Result := FLexer.ScopedEnums; +end; + +procedure TmwSimplePasPar.GotoStatement; +begin + Expected(ptGoto); + LabelId; +end; + +function TmwSimplePasPar.GetGenID: TptTokenKind; +begin + Result := FLexer.GenID; +end; + +function TmwSimplePasPar.GetInRound: Boolean; +begin + Result := FInRound > 0; +end; + +function TmwSimplePasPar.GetOnComment: TCommentEvent; +begin + Result := FLexer.OnComment; +end; + +procedure TmwSimplePasPar.SynError(Error: TmwParseError); +begin + if Assigned(FOnMessage) then + FOnMessage(Self, meError, ParserErrorName(Error) + ' found ' + FLexer.Token, FLexer.PosXY.X, + FLexer.PosXY.Y); + +end; + +(****************************************************************************** + This part is oriented at the official grammar of Delphi 4 + and parialy based on Robert Zierers Delphi grammar. + For more information about Delphi grammars take a look at: + http://www.stud.mw.tu-muenchen.de/~rz1/Grammar.html +******************************************************************************) + +procedure TmwSimplePasPar.ParseFile; +begin + SkipJunk; + case GenID of + ptLibrary: + begin + LibraryFile; + end; + ptPackage: + begin + PackageFile; + end; + ptProgram: + begin + ProgramFile; + end; + ptUnit: + begin + UnitFile; + end; + else + begin + IncludeFile; + end; + end; +end; + +procedure TmwSimplePasPar.LibraryFile; +begin + Expected(ptLibrary); + UnitName; + Semicolon; + + LibraryBlock; + Expected(ptPoint); +end; + +procedure TmwSimplePasPar.LibraryBlock; +begin + if TokenID = ptUses then + MainUsesClause; + + DeclarationSections; + + if TokenID = ptBegin then + CompoundStatement + else + Expected(ptEnd); +end; + +procedure TmwSimplePasPar.PackageFile; +begin + ExpectedEx(ptPackage); + UnitName; + Semicolon; + case ExID of + ptRequires: + begin + RequiresClause; + end; + end; + case ExID of + ptContains: + begin + ContainsClause; + end; + end; + + while Lexer.TokenID = ptSquareOpen do + begin + CustomAttribute; + end; + + Expected(ptEnd); + Expected(ptPoint); +end; + +procedure TmwSimplePasPar.ProgramFile; +begin + Expected(ptProgram); + UnitName; + if TokenID = ptRoundOpen then + begin + NextToken; + IdentifierList; + Expected(ptRoundClose); + end; + if not InterfaceOnly then + begin + Semicolon; + ProgramBlock; + Expected(ptPoint); + end; +end; + +procedure TmwSimplePasPar.UnaryMinus; +begin + Expected(ptMinus); +end; + +procedure TmwSimplePasPar.UnitFile; +begin + Expected(ptUnit); + UnitName; + TypeDirective; + + Semicolon; + InterfaceSection; + if not InterfaceOnly then + begin + ImplementationSection; + case TokenID of + ptInitialization: + begin + InitializationSection; + if TokenID = ptFinalization then + FinalizationSection; + Expected(ptEnd); + end; + ptBegin: + begin + CompoundStatement; + end; + ptEnd: + begin + NextToken; + end; + end; + + Expected(ptPoint); + end; +end; + +procedure TmwSimplePasPar.ProgramBlock; +begin + if TokenID = ptUses then + begin + MainUsesClause; + end; + Block; +end; + +procedure TmwSimplePasPar.MainUsesClause; +begin + Expected(ptUses); + MainUsedUnitStatement; + while TokenID = ptComma do + begin + NextToken; + MainUsedUnitStatement; + end; + Semicolon; +end; + +procedure TmwSimplePasPar.MethodKind; +begin + case TokenID of + ptConstructor: + begin + NextToken; + end; + ptDestructor: + begin + NextToken; + end; + ptProcedure: + begin + NextToken; + end; + ptFunction: + begin + NextToken; + end; + else + begin + SynError(InvalidProcedureMethodDeclaration); + end; + end; +end; + +procedure TmwSimplePasPar.MainUsedUnitStatement; +begin + MainUsedUnitName; + if Lexer.TokenID = ptIn then + begin + NextToken; + MainUsedUnitExpression; + end; +end; + +procedure TmwSimplePasPar.MainUsedUnitName; +begin + UsedUnitName; +end; + +procedure TmwSimplePasPar.MainUsedUnitExpression; +begin + ConstantExpression; +end; + +procedure TmwSimplePasPar.UsesClause; +begin + Expected(ptUses); + UsedUnitsList; + Semicolon; +end; + +procedure TmwSimplePasPar.UsedUnitsList; +begin + UsedUnitName; + while TokenID = ptComma do + begin + NextToken; + UsedUnitName; + end; +end; + +procedure TmwSimplePasPar.UsedUnitName; +begin + UnitId; + while Lexer.TokenID = ptPoint do + begin + NextToken; + UnitId; + end; +end; + +procedure TmwSimplePasPar.Block; +begin + DeclarationSections; + case TokenID of + ptAsm: + begin + AsmStatement; + end; + else + begin + CompoundStatement; + end; + end; +end; + +procedure TmwSimplePasPar.DeclarationSection; +begin + case TokenID of + ptClass: + begin + ProcedureDeclarationSection; + end; + ptConst: + begin + ConstSection; + end; + ptConstructor: + begin + ProcedureDeclarationSection; + end; + ptDestructor: + begin + ProcedureDeclarationSection; + end; + ptExports: + begin + ExportsClause; + end; + ptFunction: + begin + ProcedureDeclarationSection; + end; + ptLabel: + begin + LabelDeclarationSection; + end; + ptProcedure: + begin + ProcedureDeclarationSection; + end; + ptResourceString: + begin + ConstSection; + end; + ptType: + begin + TypeSection; + end; + ptThreadVar: + begin + VarSection; + end; + ptVar: + begin + VarSection; + end; + ptSquareOpen: + begin + CustomAttribute; + end; + else + begin + SynError(InvalidDeclarationSection); + end; + end; +end; + +procedure TmwSimplePasPar.UnitId; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.UnitName; +begin + UnitId; + while Lexer.TokenID = ptPoint do + begin + NextToken; + UnitId; + end; +end; + +procedure TmwSimplePasPar.InterfaceHeritage; +begin + Expected(ptRoundOpen); + AncestorIdList; + Expected(ptRoundClose); +end; + +procedure TmwSimplePasPar.InterfaceGUID; +begin + Expected(ptSquareOpen); + CharString; + Expected(ptSquareClose); +end; + +procedure TmwSimplePasPar.AccessSpecifier; +begin + case ExID of + ptRead: + begin + NextToken; + ReadAccessIdentifier; + end; + ptWrite: + begin + NextToken; + WriteAccessIdentifier; + end; + ptReadOnly: + begin + NextToken; + end; + ptWriteOnly: + begin + NextToken; + end; + ptAdd: + begin + NextToken; + QualifiedIdentifier; //TODO: AddAccessIdentifier + end; + ptRemove: + begin + NextToken; + QualifiedIdentifier; //TODO: RemoveAccessIdentifier + end; + else + begin + SynError(InvalidAccessSpecifier); + end; + end; +end; + +procedure TmwSimplePasPar.ReadAccessIdentifier; +begin + variable; +end; + +procedure TmwSimplePasPar.WriteAccessIdentifier; +begin + variable; +end; + +procedure TmwSimplePasPar.StorageSpecifier; +begin + case ExID of + ptStored: + begin + StorageStored; + end; + ptDefault: + begin + StorageDefault; + end; + ptNoDefault: + begin + StorageNoDefault; + end + else + begin + SynError(InvalidStorageSpecifier); + end; + end; +end; + +procedure TmwSimplePasPar.StorageDefault; +begin + ExpectedEx(ptDefault); + StorageExpression; +end; + +procedure TmwSimplePasPar.StorageNoDefault; +begin + ExpectedEx(ptNoDefault); +end; + +procedure TmwSimplePasPar.StorageStored; +begin + ExpectedEx(ptStored); + case TokenID of + ptIdentifier: + begin + StorageIdentifier; + end; + else + if TokenID <> ptSemiColon then + begin + StorageExpression; + end; + end; +end; + +procedure TmwSimplePasPar.StorageExpression; +begin + ConstantExpression; +end; + +procedure TmwSimplePasPar.StorageIdentifier; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.PropertyParameterList; +begin + Expected(ptSquareOpen); + FormalParameterSection; + while TokenID = ptSemiColon do + begin + Semicolon; + FormalParameterSection; + end; + Expected(ptSquareClose); +end; + +procedure TmwSimplePasPar.PropertySpecifiers; +begin + if ExID = ptIndex then + begin + IndexSpecifier; + end; + while ExID in [ptRead, ptReadOnly, ptWrite, ptWriteOnly, ptAdd, ptRemove] do + begin + AccessSpecifier; + if TokenID = ptSemicolon then + NextToken; + end; + if ExID = ptDispId then + begin + DispIDSpecifier; + end; + while ExID in [ptDefault, ptNoDefault, ptStored] do + begin + StorageSpecifier; + if TokenID = ptSemicolon then + NextToken; + end; + if ExID = ptImplements then + begin + ImplementsSpecifier; + end; + if TokenID = ptSemicolon then + NextToken; +end; + +procedure TmwSimplePasPar.PropertyInterface; +begin + if TokenID = ptSquareOpen then + begin + PropertyParameterList; + end; + Expected(ptColon); + TypeID; +end; + +procedure TmwSimplePasPar.ClassMethodHeading; +begin + if TokenID = ptClass then + ClassClass; + + InitAhead; + AheadParse.NextToken; + AheadParse.FunctionProcedureName; + + if AheadParse.TokenId = ptEqual then + ClassMethodResolution + else + begin + case TokenID of + ptConstructor: + begin + ConstructorHeading; + end; + ptDestructor: + begin + DestructorHeading; + end; + ptFunction: + begin + ClassFunctionHeading; + end; + ptProcedure: + begin + ClassProcedureHeading; + end; + ptIdentifier: + begin + if Lexer.ExID = ptOperator then + begin + ClassOperatorHeading; + end + else + SynError(InvalidProcedureMethodDeclaration); + end; + else + SynError(InvalidClassMethodHeading); + end; + end; +end; + +procedure TmwSimplePasPar.ClassFunctionHeading; +begin + Expected(ptFunction); + FunctionProcedureName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + Expected(ptColon); + ReturnType; + if TokenId = ptSemicolon then + Semicolon; + if ExID in ClassMethodDirectiveEnum then + ClassMethodDirective; +end; + +procedure TmwSimplePasPar.FunctionMethodName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.ClassProcedureHeading; +begin + Expected(ptProcedure); + FunctionProcedureName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + if TokenId = ptSemicolon then + Semicolon; + + if ExID = ptDispId then + begin + DispIDSpecifier; + if TokenId = ptSemicolon then + Semicolon; + end; + if exID in ClassMethodDirectiveEnum then + ClassMethodDirective; +end; + +procedure TmwSimplePasPar.ProcedureMethodName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.ClassMethodResolution; +begin + case TokenID of + ptFunction: + begin + NextToken; + end; + ptProcedure: + begin + NextToken; + end; + ptIdentifier: + begin + if Lexer.ExID = ptOperator then + NextToken; + end; + end; + FunctionProcedureName; + Expected(ptEqual); + FunctionMethodName; + Semicolon; +end; + +procedure TmwSimplePasPar.ClassOperatorHeading; +begin + ExpectedEx(ptOperator); + FunctionProcedureName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + + if TokenID = ptColon then + begin + Expected(ptColon); + ReturnType; + end; + + if TokenId = ptSemicolon then + Semicolon; + if ExID in ClassMethodDirectiveEnum then + ClassMethodDirective; +end; + +procedure TmwSimplePasPar.ResolutionInterfaceName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.Constraint; +begin + while TokenId in [ptConstructor, ptRecord, ptClass, ptIdentifier] do + begin + case TokenId of + ptConstructor: ConstructorConstraint; + ptRecord: RecordConstraint; + ptClass: ClassConstraint; + ptIdentifier: TypeId; + end; + if TokenId = ptComma then + NextToken; + end; +end; + +procedure TmwSimplePasPar.ConstraintList; +begin + Constraint; + while TokenId = ptComma do + begin + Constraint; + end; +end; + +procedure TmwSimplePasPar.ConstructorConstraint; +begin + Expected(ptConstructor); +end; + +procedure TmwSimplePasPar.ConstructorHeading; +begin + Expected(ptConstructor); + ConstructorName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + if TokenID = ptSemiColon then Semicolon; + ClassMethodDirective; +end; + +procedure TmwSimplePasPar.ConstructorName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.DestructorHeading; +begin + Expected(ptDestructor); + DestructorName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + if TokenID = ptSemiColon then Semicolon; + ClassMethodDirective; +end; + +procedure TmwSimplePasPar.DestructorName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.ClassMethod; +begin + Expected(ptClass); +end; + +procedure TmwSimplePasPar.ClassMethodDirective; +begin + while ExId in ClassMethodDirectiveEnum do + begin + if ExID = ptDispId then + DispIDSpecifier + else + ProceduralDirective; + if TokenId = ptSemicolon then + Semicolon; + end; +end; + +procedure TmwSimplePasPar.ObjectMethodHeading; +begin + case TokenID of + ptConstructor: + begin + ObjectConstructorHeading; + end; + ptDestructor: + begin + ObjectDestructorHeading; + end; + ptFunction: + begin + ObjectFunctionHeading; + end; + ptProcedure: + begin + ObjectProcedureHeading; + end; + else + begin + SynError(InvalidMethodHeading); + end; + end; +end; + +procedure TmwSimplePasPar.ObjectFunctionHeading; +begin + Expected(ptFunction); + FunctionMethodName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + Expected(ptColon); + ReturnType; + if TokenID = ptSemiColon then Semicolon; + ObjectMethodDirective; +end; + +procedure TmwSimplePasPar.ObjectProcedureHeading; +begin + Expected(ptProcedure); + ProcedureMethodName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + if TokenID = ptSemiColon then Semicolon; + ObjectMethodDirective; +end; + +procedure TmwSimplePasPar.ObjectConstructorHeading; +begin + Expected(ptConstructor); + ConstructorName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + if TokenID = ptSemiColon then Semicolon; + ObjectMethodDirective; +end; + +procedure TmwSimplePasPar.ObjectDestructorHeading; +begin + Expected(ptDestructor); + DestructorName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + if TokenID = ptSemiColon then Semicolon; + ObjectMethodDirective; +end; + +procedure TmwSimplePasPar.ObjectMethodDirective; +begin + while ExID in [ptAbstract, ptCdecl, ptDynamic, ptExport, ptExternal, ptFar, + ptMessage, ptNear, ptOverload, ptPascal, ptRegister, ptSafeCall, ptStdCall, + ptVirtual, ptDeprecated, ptLibrary, ptPlatform, ptStatic, ptInline] do + begin + ProceduralDirective; + if TokenID = ptSemiColon then Semicolon; + end; +end; + +procedure TmwSimplePasPar.Directive16Bit; +begin + case ExID of + ptNear: + begin + NextToken; + end; + ptFar: + begin + NextToken; + end; + ptExport: + begin + NextToken; + end; + else + begin + SynError(InvalidDirective16Bit); + end; + end; +end; + +procedure TmwSimplePasPar.DirectiveBinding; +begin + case ExID of + ptAbstract: + begin + NextToken; + end; + ptVirtual: + begin + NextToken; + end; + ptDynamic: + begin + NextToken; + end; + ptMessage: + begin + DirectiveBindingMessage; + end; + ptOverride: + begin + NextToken; + end; + ptOverload: + begin + NextToken; + end; + ptReintroduce: + begin + NextToken; + end; + ptNoreturn: + begin + NextToken; + end; + else + begin + SynError(InvalidDirectiveBinding); + end; + end; +end; + +procedure TmwSimplePasPar.DirectiveBindingMessage; +begin + NextToken; + ConstantExpression; +end; + +procedure TmwSimplePasPar.ReturnType; +begin + while TokenID = ptSquareOpen do + CustomAttribute; + + TypeID; +end; + +procedure TmwSimplePasPar.RoundClose; +begin + Expected(ptRoundClose); + Dec(FInRound); +end; + +procedure TmwSimplePasPar.RoundOpen; +begin + Expected(ptRoundOpen); + Inc(FInRound); +end; + +procedure TmwSimplePasPar.FormalParameterList; +begin + Expected(ptRoundOpen); + FormalParameterSection; + while TokenID = ptSemiColon do + begin + Semicolon; + FormalParameterSection; + end; + Expected(ptRoundClose); +end; + +procedure TmwSimplePasPar.FormalParameterSection; +begin + while TokenID = ptSquareOpen do + CustomAttribute; + case TokenID of + ptConst: + begin + ConstParameter; + end; + ptIdentifier: + case ExID of + ptOut: OutParameter; + else + ParameterFormal; + end; + ptIn: + begin + InParameter; + end; + ptVar: + begin + VarParameter; + end; + end; +end; + +procedure TmwSimplePasPar.ConstParameter; +begin + Expected(ptConst); + ParameterNameList; + case TokenID of + ptColon: + begin + NextToken; + FormalParameterType; + if TokenID = ptEqual then + begin + NextToken; + TypedConstant; + end; + end + end; +end; + +procedure TmwSimplePasPar.VarParameter; +begin + Expected(ptVar); + ParameterNameList; + case TokenID of + ptColon: + begin + NextToken; + FormalParameterType; + end + end; +end; + +procedure TmwSimplePasPar.OutParameter; +begin + ExpectedEx(ptOut); + ParameterNameList; + case TokenID of + ptColon: + begin + NextToken; + FormalParameterType; + end + end; +end; + +procedure TmwSimplePasPar.ParameterFormal; +begin + case TokenID of + ptIdentifier: + begin + ParameterNameList; + Expected(ptColon); + FormalParameterType; + if TokenID = ptEqual then + begin + NextToken; + TypedConstant; + end; + end; + else + begin + SynError(InvalidParameter); + end; + end; +end; + +procedure TmwSimplePasPar.ParameterNameList; +begin + while TokenID = ptSquareOpen do + CustomAttribute; + ParameterName; + + while TokenID = ptComma do + begin + NextToken; + + while TokenID = ptSquareOpen do + CustomAttribute; + ParameterName; + end; +end; + +procedure TmwSimplePasPar.ParameterName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.FormalParameterType; +begin + if TokenID = ptArray then + StructuredType + else + TypeID; +end; + +procedure TmwSimplePasPar.FunctionMethodDeclaration; +begin + if (TokenID = ptIdentifier) and (Lexer.ExID = ptOperator) then + NextToken else + MethodKind; + FunctionProcedureName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + case TokenID of + ptSemiColon: + begin + FunctionProcedureBlock; + end; + else + begin + Expected(ptColon); + ReturnType; + FunctionProcedureBlock; + end; + end; +end; + +procedure TmwSimplePasPar.ProcedureProcedureName; +begin + MethodKind; + FunctionProcedureName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + FunctionProcedureBlock; +end; + +procedure TmwSimplePasPar.FunctionProcedureName; +begin + ObjectNameOfMethod; +end; + +procedure TmwSimplePasPar.ObjectNameOfMethod; +begin + if TokenID = ptIn then + Expected(ptIn) + else + Expected(ptIdentifier); + + if TokenId = ptLower then + TypeParams; + if TokenID = ptPoint then + begin + Expected(ptPoint); + ObjectNameOfMethod; + end; +end; + +procedure TmwSimplePasPar.FunctionProcedureBlock; +var + HasBlock: Boolean; +begin + HasBlock := True; + if TokenID = ptSemiColon then Semicolon; + + while ExID in [ptAbstract, ptCdecl, ptDynamic, ptExport, ptExternal, ptDelayed, ptFar, + ptMessage, ptNear, ptOverload, ptOverride, ptPascal, ptRegister, + ptReintroduce, ptSafeCall, ptStdCall, ptVirtual, ptLibrary, + ptPlatform, ptLocal, ptVarargs, ptAssembler, ptStatic, ptInline, ptForward, + ptExperimental, ptDeprecated, ptNoreturn] do + begin + case ExId of + ptExternal: + begin + ProceduralDirective; + HasBlock := False; + end; + ptForward: + begin + ForwardDeclaration; + HasBlock := False; + end + else + begin + ProceduralDirective; + end; + end; + if TokenID = ptSemiColon then Semicolon; + end; + + if HasBlock then + begin + case TokenID of + ptAsm: + begin + AsmStatement; + end; + else + begin + Block; + end; + end; + Semicolon; + end; +end; + +procedure TmwSimplePasPar.ExternalDirective; +begin + ExpectedEx(ptExternal); + case TokenID of + ptSemiColon: + begin + Semicolon; + end; + else + begin + if FLexer.ExID <> ptName then + SimpleExpression; + + if FLexer.ExID = ptDelayed then + NextToken; + + ExternalDirectiveTwo; + end; + end; +end; + +procedure TmwSimplePasPar.ExternalDirectiveTwo; +begin + case FLexer.ExID of + ptIndex: + begin + IndexSpecifier; + end; + ptName: + begin + NextToken; + SimpleExpression; + end; + ptSemiColon: + begin + Semicolon; + ExternalDirectiveThree; + end; + end +end; + +procedure TmwSimplePasPar.ExternalDirectiveThree; +begin + case TokenID of + ptMinus: + begin + NextToken; + end; + end; + case TokenID of + ptIdentifier, ptIntegerConst: + begin + NextToken; + end; + end; +end; + +procedure TmwSimplePasPar.ForStatement; +begin + Expected(ptFor); + if TokenID = ptVar then + begin + NextToken; + InlineVarDeclaration; + end + else + QualifiedIdentifier; + + if Lexer.TokenID = ptAssign then + begin + Expected(ptAssign); + ForStatementFrom; + case TokenID of + ptTo: + begin + ForStatementTo; + end; + ptDownTo: + begin + ForStatementDownTo; + end; + else + begin + SynError(InvalidForStatement); + end; + end; + end else + if Lexer.TokenID = ptIn then + ForStatementIn; + Expected(ptDo); + Statement; +end; + +procedure TmwSimplePasPar.ForStatementDownTo; +begin + Expected(ptDownTo); + Expression; +end; + +procedure TmwSimplePasPar.ForStatementFrom; +begin + Expression; +end; + +procedure TmwSimplePasPar.ForStatementIn; +begin + Expected(ptIn); + Expression; +end; + +procedure TmwSimplePasPar.ForStatementTo; +begin + Expected(ptTo); + Expression; +end; + +procedure TmwSimplePasPar.WhileStatement; +begin + Expected(ptWhile); + Expression; + Expected(ptDo); + Statement; +end; + +procedure TmwSimplePasPar.RepeatStatement; +begin + Expected(ptRepeat); + StatementList; + Expected(ptUntil); + Expression; +end; + +procedure TmwSimplePasPar.CaseStatement; +begin + Expected(ptCase); + Expression; + Expected(ptOf); + CaseSelector; + while TokenID = ptSemiColon do + begin + Semicolon; + case TokenID of + ptElse, ptEnd: ; + else + CaseSelector; + end; + end; + if TokenID = ptElse then + CaseElseStatement; + Expected(ptEnd); +end; + +procedure TmwSimplePasPar.CaseSelector; +begin + CaseLabelList; + Expected(ptColon); + case TokenID of + ptSemiColon: EmptyStatement; + else + Statement; + end; +end; + +procedure TmwSimplePasPar.CaseElseStatement; +begin + Expected(ptElse); + StatementList; + Semicolon; +end; + +procedure TmwSimplePasPar.CaseLabel; +begin + ConstantExpression; + if TokenID = ptDotDot then + begin + NextToken; + ConstantExpression; + end; +end; + +procedure TmwSimplePasPar.IfStatement; +begin + Expected(ptIf); + Expression; + ThenStatement; + if TokenID = ptElse then + ElseStatement; +end; + +procedure TmwSimplePasPar.TernaryOp; +begin + Expected(ptIf); + Expression; + ThenExpression; + ElseExpression; +end; + +procedure TmwSimplePasPar.ExceptBlock; +begin + if ExID = ptOn then + begin + ExceptionHandlerList; + if TokenID = ptElse then + ExceptionBlockElseBranch; + end else + if TokenID = ptElse then + ExceptionBlockElseBranch + else + StatementList; +end; + +procedure TmwSimplePasPar.ExceptionHandlerList; +begin + while FLexer.ExID = ptOn do + begin + ExceptionHandler; + Semicolon; + end; +end; + +procedure TmwSimplePasPar.ExceptionHandler; +begin + ExpectedEx(ptOn); + ExceptionIdentifier; + Expected(ptDo); + Statement; +end; + +procedure TmwSimplePasPar.ExceptionBlockElseBranch; +begin + NextToken; + StatementList; +end; + +procedure TmwSimplePasPar.ExceptionIdentifier; +begin + Lexer.InitAhead; + case Lexer.AheadTokenID of + ptPoint: + begin + ExceptionClassTypeIdentifier; + end; + ptColon: + begin + ExceptionVariable; + end + else + begin + ExceptionClassTypeIdentifier; + end; + end; +end; + +procedure TmwSimplePasPar.ExceptionClassTypeIdentifier; +begin + TypeKind; +end; + +procedure TmwSimplePasPar.ExceptionVariable; +begin + Expected(ptIdentifier); + Expected(ptColon); + ExceptionClassTypeIdentifier; +end; + +procedure TmwSimplePasPar.InlineConstSection; +begin + case TokenID of + ptConst: + begin + NextToken; + ConstantDeclaration; + end; + else + begin + SynError(InvalidConstSection); + end; + end; +end; + +procedure TmwSimplePasPar.InlineStatement; +begin + Expected(ptInline); + Expected(ptRoundOpen); + Expected(ptIntegerConst); + while (TokenID = ptSlash) do + begin + NextToken; + Expected(ptIntegerConst); + end; + Expected(ptRoundClose); +end; + +procedure TmwSimplePasPar.InlineVarSection; +begin + Expected(ptVar); + while TokenID = ptIdentifier do + InlineVarDeclaration; + + if TokenID = ptAssign then + begin + NextToken; + Expression; + end; +end; + +procedure TmwSimplePasPar.InlineVarDeclaration; +begin + VarNameList; + if TokenID = ptColon then + begin + NextToken; + TypeKind; + end; +end; + +procedure TmwSimplePasPar.InParameter; +begin + Expected(ptIn); + ParameterNameList; + case TokenID of + ptColon: + begin + NextToken; + FormalParameterType; + if TokenID = ptEqual then + begin + NextToken; + TypedConstant; + end; + end + end; +end; + +procedure TmwSimplePasPar.AsmStatement; +begin + Lexer.AsmCode := True; + Expected(ptAsm); + { should be replaced with a Assembler lexer } + while TokenID <> ptEnd do + case FLexer.TokenID of + ptAddressOp: + begin + NextToken; + NextToken; + end; + ptDoubleAddressOp: + begin + NextToken; + NextToken; + end; + ptNull: + begin + Expected(ptEnd); + Exit; + end; + else + NextToken; + end; + Lexer.AsmCode := False; + Expected(ptEnd); +end; + +procedure TmwSimplePasPar.AsOp; +begin + Expected(ptAs); +end; + +procedure TmwSimplePasPar.AssignOp; +begin + Expected(ptAssign); +end; + +procedure TmwSimplePasPar.AtExpression; +begin + ExpectedEx(ptAt); + Expression; +end; + +procedure TmwSimplePasPar.RaiseStatement; +begin + Expected(ptRaise); + case TokenID of + ptAddressOp, ptDoubleAddressOp, ptIdentifier, ptRoundOpen: + begin + Expression; + end; + end; + if ExID = ptAt then + AtExpression; +end; + +procedure TmwSimplePasPar.TryStatement; +begin + Expected(ptTry); + StatementList; + case TokenID of + ptExcept: + begin + NextToken; + ExceptBlock; + Expected(ptEnd); + end; + ptFinally: + begin + NextToken; + FinallyBlock; + Expected(ptEnd); + end; + else + begin + SynError(InvalidTryStatement); + end; + end; +end; + +procedure TmwSimplePasPar.WithStatement; +begin + Expected(ptWith); + WithExpressionList; + Expected(ptDo); + Statement; +end; + +procedure TmwSimplePasPar.WithExpressionList; +begin + Expression; + while FLexer.TokenID = ptComma do + begin + NextToken; + Expression; + end; +end; + +procedure TmwSimplePasPar.StatementList; +begin + Statements; +end; + +procedure TmwSimplePasPar.StatementOrExpression; +begin + if TokenID = ptGoto then + SimpleStatement + else + begin + InitAhead; + AheadParse.Designator; + + if AheadParse.TokenId in [ptAssign, ptSemicolon, ptElse] then + SimpleStatement + else + Expression; + end; +end; + +procedure TmwSimplePasPar.Statements; +begin {removed ptIntegerConst jdj-Put back in for labels} + while TokenID in [ptAddressOp, ptAsm, ptBegin, ptCase, ptConst, ptDoubleAddressOp, + ptFor, ptGoTo, ptIdentifier, ptIf, ptInherited, ptInline, ptIntegerConst, + ptPointerSymbol, ptRaise, ptRoundOpen, ptRepeat, ptSemiColon, ptString, + ptTry, ptVar, ptWhile, ptWith] do + begin + Statement; + Semicolon; + end; +end; + +procedure TmwSimplePasPar.SimpleStatement; +begin + case TokenID of + ptAddressOp, ptDoubleAddressOp, ptIdentifier, ptRoundOpen, ptString: + begin + Designator; + if TokenID = ptAssign then + begin + AssignOp; + Expression; + end; + end; + ptGoTo: + begin + GotoStatement; + end; + end; +end; + +procedure TmwSimplePasPar.Statement; +begin + case TokenID of + ptAsm: + begin + AsmStatement; + end; + ptBegin: + begin + CompoundStatement; + end; + ptCase: + begin + CaseStatement; + end; + ptConst: + begin + InlineConstSection; + end; + ptFor: + begin + ForStatement; + end; + ptIf: + begin + IfStatement; + end; + ptIdentifier: + begin + FLexer.InitAhead; + case Lexer.AheadTokenID of + ptColon: + begin + LabeledStatement; + end; + else + begin + StatementOrExpression; + end; + end; + end; + ptInherited: + begin + InheritedStatement; + end; + ptInLine: + begin + InlineStatement; + end; + ptIntegerConst: + begin + FLexer.InitAhead; + case Lexer.AheadTokenID of + ptColon: + begin + LabeledStatement; + end; + else + begin + SynError(InvalidLabeledStatement); + NextToken; + end; + end; + end; + ptRepeat: + begin + RepeatStatement; + end; + ptRaise: + begin + RaiseStatement; + end; + ptSemiColon: + begin + EmptyStatement; + end; + ptTry: + begin + TryStatement; + end; + ptVar: + begin + InlineVarSection; + end; + ptWhile: + begin + WhileStatement; + end; + ptWith: + begin + WithStatement; + end; + else + begin + StatementOrExpression; + end; + end; +end; + +procedure TmwSimplePasPar.ElseExpression; +begin + Expected(ptElse); + Expression; +end; + +procedure TmwSimplePasPar.ElseStatement; +begin + Expected(ptElse); + Statement; +end; + +procedure TmwSimplePasPar.EmptyStatement; +begin + { Nothing to do here. + The semicolon will be removed in StatementList } +end; + +procedure TmwSimplePasPar.InheritedStatement; +begin + Expected(ptInherited); + if TokenID = ptIdentifier then + Statement; +end; + +procedure TmwSimplePasPar.LabeledStatement; +begin + case TokenID of + ptIdentifier: + begin + NextToken; + Expected(ptColon); + Statement; + end; + ptIntegerConst: + begin + NextToken; + Expected(ptColon); + Statement; + end; + else + begin + SynError(InvalidLabeledStatement); + end; + end; +end; + +procedure TmwSimplePasPar.StringStatement; +begin + Expected(ptString); +end; + +procedure TmwSimplePasPar.SetElement; +begin + Expression; + if TokenID = ptDotDot then + begin + NextToken; + Expression; + end; +end; + +procedure TmwSimplePasPar.SetIncludeHandler(IncludeHandler: IIncludeHandler); +begin + FLexer.IncludeHandler := IncludeHandler; +end; + +procedure TmwSimplePasPar.SetOnComment(const Value: TCommentEvent); +begin + FLexer.OnComment := Value; +end; + +procedure TmwSimplePasPar.QualifiedIdentifier; +begin + Identifier; + + while TokenID = ptPoint do + begin + DotOp; + Identifier; + end; +end; + +procedure TmwSimplePasPar.SetConstructor; +begin + Expected(ptSquareOpen); + if TokenID <> ptSquareClose then + begin + SetElement; + while TokenID = ptComma do + begin + NextToken; + SetElement; + end; + end; + Expected(ptSquareClose); +end; + +procedure TmwSimplePasPar.Number; +begin + case TokenID of + ptFloat: + begin + NextToken; + end; + ptIntegerConst: + begin + NextToken; + end; + ptIdentifier: + begin + NextToken; + end; + else + begin + SynError(InvalidNumber); + end; + end; +end; + +procedure TmwSimplePasPar.ExpressionList; +begin + Expression; + if TokenID = ptAssign then + begin + Expected(ptAssign); + Expression; + end; + while TokenID = ptComma do + begin + NextToken; + Expression; + if TokenID = ptAssign then + begin + Expected(ptAssign); + Expression; + end; + end; +end; + +procedure TmwSimplePasPar.Designator; +begin + VariableReference; +end; + +procedure TmwSimplePasPar.MultiplicativeOperator; +begin + case TokenID of + ptAnd: + begin + NextToken; + end; + ptDiv: + begin + NextToken; + end; + ptMod: + begin + NextToken; + end; + ptShl: + begin + NextToken; + end; + ptShr: + begin + NextToken; + end; + ptSlash: + begin + NextToken; + end; + ptStar: + begin + NextToken; + end; + else + begin SynError(InvalidMultiplicativeOperator); + end; + end; +end; + +procedure TmwSimplePasPar.Factor; +begin + case TokenID of + ptIf: + begin + TernaryOp; + end; + ptAsciiChar, ptStringConst: + begin + CharString; + end; + ptAddressOp, ptDoubleAddressOp, ptIdentifier, ptInherited, ptPointerSymbol: + begin + Designator; + end; + ptRoundOpen: + begin + RoundOpen; + ExpressionList; + RoundClose; + end; + ptIntegerConst, ptFloat: + begin + Number; + end; + ptNil: + begin + NilToken; + end; + ptMinus: + begin + UnaryMinus; + Factor; + end; + ptNot: + begin + NotOp; + Factor; + end; + ptPlus: + begin + NextToken; + Factor; + end; + ptSquareOpen: + begin + SetConstructor; + end; + ptString: + begin + StringStatement; + end; + ptFunction, ptProcedure: + AnonymousMethod; + end; + + while TokenID = ptSquareOpen do + IndexOp; + + while TokenID = ptPointerSymbol do + PointerSymbol; + + if TokenID = ptRoundOpen then + Factor; + + while TokenID = ptPoint do + begin + DotOp; + Factor; + end; +end; + +procedure TmwSimplePasPar.AdditiveOperator; +begin + if TokenID in [ptMinus, ptOr, ptPlus, ptXor] then + begin + NextToken; + end + else + begin + SynError(InvalidAdditiveOperator); + end; +end; + +procedure TmwSimplePasPar.AddressOp; +begin + Expected(ptAddressOp); +end; + +procedure TmwSimplePasPar.AlignmentParameter; +begin + SimpleExpression; +end; + +procedure TmwSimplePasPar.Term; +begin + Factor; + while TokenID in [ptAnd, ptDiv, ptMod, ptShl, ptShr, ptSlash, ptStar] do + begin + MultiplicativeOperator; + Factor; + end; +end; + +procedure TmwSimplePasPar.RelativeOperator; +begin + case TokenID of + ptAs: + begin + NextToken; + end; + ptEqual: + begin + NextToken; + end; + ptGreater: + begin + NextToken; + end; + ptGreaterEqual: + begin + NextToken; + end; + ptIn: + begin + NextToken; + end; + ptIs: + begin + NextToken; + end; + ptLower: + begin + NextToken; + end; + ptLowerEqual: + begin + NextToken; + end; + ptNotEqual: + begin + NextToken; + end; + else + begin + SynError(InvalidRelativeOperator); + end; + end; +end; + +procedure TmwSimplePasPar.SimpleExpression; +begin + Term; + while TokenID in [ptMinus, ptOr, ptPlus, ptXor] do + begin + AdditiveOperator; + Term; + end; + + case TokenID of + ptAs: + begin + AsOp; + TypeId; + end; + end; +end; + +procedure TmwSimplePasPar.Expression; +begin + SimpleExpression; + + //JT 2006-07-17 The Delphi language guide has this as + //Expression -> SimpleExpression [RelOp SimpleExpression]... + //So this needs to be able to repeat itself. + case TokenID of + ptEqual, ptGreater, ptGreaterEqual, ptLower, ptLowerEqual, ptIn, + ptNotEqual, ptNot, ptIs: + begin + while TokenID in [ptEqual, ptGreater, ptGreaterEqual, ptLower, ptLowerEqual, + ptIn, ptNotEqual{, ptColon}, ptNot, ptIs] do + begin + if TokenID = ptNot then + begin + Lexer.InitAhead; + if Lexer.AheadTokenID = ptIn then + begin + NotInOp; + SimpleExpression; + Continue; + end; + end; + + if TokenID = ptIs then + begin + Lexer.InitAhead; + if Lexer.AheadTokenID = ptNot then + begin + IsNotOp; + SimpleExpression; + Continue; + end; + end; + + RelativeOperator; + SimpleExpression; + end; + end; + ptColon: + begin + case InRound of + False: ; + True: + while TokenID = ptColon do + begin + NextToken; + AlignmentParameter; + end; + end; + end; + end; +end; + +procedure TmwSimplePasPar.VarDeclaration; +begin + VarNameList; + Expected(ptColon); + TypeKind; + TypeDirective; + + case GenID of + ptAbsolute: + begin + VarAbsolute; + end; + ptEqual: + begin + VarEqual; + end; + end; + TypeDirective; +end; + +procedure TmwSimplePasPar.VarAbsolute; +begin + ExpectedEx(ptAbsolute); + ConstantValue; +end; + +procedure TmwSimplePasPar.VarEqual; +begin + Expected(ptEqual); + ConstantValueTyped; +end; + +procedure TmwSimplePasPar.VarNameList; +begin + VarName; + while TokenID = ptComma do + begin + NextToken; + VarName; + end; +end; + +procedure TmwSimplePasPar.VarName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.DirectiveCalling; +begin + case ExID of + ptCdecl: + begin + NextToken; + end; + ptPascal: + begin + NextToken; + end; + ptRegister: + begin + NextToken; + end; + ptSafeCall: + begin + NextToken; + end; + ptStdCall: + begin + NextToken; + end; + else + begin + SynError(InvalidDirectiveCalling); + end; + end; +end; + +procedure TmwSimplePasPar.RecordVariant; +begin + ConstantExpression; + while (TokenID = ptComma) do + begin + NextToken; + ConstantExpression; + end; + Expected(ptColon); + Expected(ptRoundOpen); + if TokenID <> ptRoundClose then + begin + FieldList; + end; + Expected(ptRoundClose); +end; + +procedure TmwSimplePasPar.VariantSection; +begin + Expected(ptCase); + TagField; + Expected(ptOf); + RecordVariant; + while TokenID = ptSemiColon do + begin + Semicolon; + case TokenID of + ptEnd, ptRoundClose: Break; + else + RecordVariant; + end; + end; +end; + +procedure TmwSimplePasPar.TagField; +begin + TagFieldName; + case FLexer.TokenID of + ptColon: + begin + NextToken; + TagFieldTypeName; + end; + end; +end; + +procedure TmwSimplePasPar.TagFieldName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.TagFieldTypeName; +begin + OrdinalType; +end; + +procedure TmwSimplePasPar.FieldDeclaration; +begin + if TokenID = ptSquareOpen then + CustomAttribute; + FieldNameList; + Expected(ptColon); + TypeKind; + TypeDirective; +end; + +procedure TmwSimplePasPar.FieldList; +begin + while TokenID in [ptIdentifier, ptSquareOpen] do + begin + FieldDeclaration; + Semicolon; + end; + if TokenID = ptCase then + begin + VariantSection; + end; +end; + +procedure TmwSimplePasPar.FieldName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.FieldNameList; +begin + FieldName; + while TokenID = ptComma do + begin + NextToken; + FieldName; + end; +end; + +procedure TmwSimplePasPar.RecordType; +begin + Expected(ptRecord); + if TokenID = ptSemicolon then + Exit; + + if ExID = ptHelper then + ClassHelper; + + if TokenID = ptRoundOpen then + begin + ClassHeritage; + if TokenID = ptSemicolon then + Exit; + end; + ClassMemberList; + Expected(ptEnd); + + ClassTypeEnd; + RecordAlign; +end; + +procedure TmwSimplePasPar.FileType; +begin + Expected(ptFile); + if TokenID = ptOf then + begin + NextToken; + TypeId; + end; +end; + +procedure TmwSimplePasPar.FinalizationSection; +begin + Expected(ptFinalization); + StatementList; +end; + +procedure TmwSimplePasPar.FinallyBlock; +begin + StatementList; +end; + +procedure TmwSimplePasPar.SetType; +begin + Expected(ptSet); + Expected(ptOf); + OrdinalType; +end; + +procedure TmwSimplePasPar.SetUseDefines(const Value: Boolean); +begin + FLexer.UseDefines := Value; +end; + +procedure TmwSimplePasPar.ArrayType; +begin + Expected(ptArray); + ArrayBounds; + Expected(ptOf); + TypeKind; +end; + +procedure TmwSimplePasPar.EnumeratedType; +begin + Expected(ptRoundOpen); + EnumeratedTypeItem; + while TokenID = ptComma do + begin + NextToken; + EnumeratedTypeItem; + end; + Expected(ptRoundClose); +end; + +procedure TmwSimplePasPar.SubrangeType; +begin + ConstantExpression; + if TokenID = ptDotDot then + begin + NextToken; + ConstantExpression; + end; +end; + +procedure TmwSimplePasPar.RealIdentifier; +begin + case ExID of + ptReal48: + begin + NextToken; + end; + ptReal: + begin + NextToken; + end; + ptSingle: + begin + NextToken; + end; + ptDouble: + begin + NextToken; + end; + ptExtended: + begin + NextToken; + end; + ptCurrency: + begin + NextToken; + end; + ptComp: + begin + NextToken; + end; + else + begin + SynError(InvalidRealIdentifier); + end; + end; +end; + +procedure TmwSimplePasPar.RealType; +begin + case TokenID of + ptMinus: + begin + NextToken; + end; + ptPlus: + begin + NextToken; + end; + end; + case TokenId of + ptFloat: + begin + NextToken; + end; + else + begin + VariableReference; + end; + end; +end; + +procedure TmwSimplePasPar.OrdinalIdentifier; +begin + case ExID of + ptBoolean: + begin + NextToken; + end; + ptByte: + begin + NextToken; + end; + ptBytebool: + begin + NextToken; + end; + ptCardinal: + begin + NextToken; + end; + ptChar: + begin + NextToken; + end; + ptDWord: + begin + NextToken; + end; + ptInt64: + begin + NextToken; + end; + ptInteger: + begin + NextToken; + end; + ptLongBool: + begin + NextToken; + end; + ptLongInt: + begin + NextToken; + end; + ptLongWord: + begin + NextToken; + end; + ptPChar: + begin + NextToken; + end; + ptShortInt: + begin + NextToken; + end; + ptSmallInt: + begin + NextToken; + end; + ptWideChar: + begin + NextToken; + end; + ptWord: + begin + NextToken; + end; + ptWordbool: + begin + NextToken; + end; + else + begin + SynError(InvalidOrdinalIdentifier); + end; + end; +end; + +procedure TmwSimplePasPar.OrdinalType; +begin + case TokenID of + ptIdentifier: + begin + Lexer.InitAhead; + case Lexer.AheadTokenID of + ptPoint: + begin + TypeId; + end; + ptRoundOpen, ptDotDot: + begin + ConstantExpression; + end; + else + begin + TypeID; + end; + end; + end; + ptRoundOpen: + begin + EnumeratedType; + end; + ptSquareOpen: + begin + NextToken; + SubrangeType; + Expected(ptSquareClose); + end; + else + begin + ConstantExpression; + end; + end; + if TokenID = ptDotDot then + begin + NextToken; + ConstantExpression; + end; +end; + +procedure TmwSimplePasPar.VariableReference; +begin + case TokenID of + ptRoundOpen: + begin + RoundOpen; + Expression; + RoundClose; + VariableTail; + end; + ptSquareOpen: + begin + SetConstructor; + end; + ptAddressOp: + begin + AddressOp; + VariableReference; + end; + ptDoubleAddressOp: + begin + NextToken; + VariableReference; + end; + ptInherited: + begin + InheritedVariableReference; + end; + else + variable; + end; +end; + +procedure TmwSimplePasPar.Variable; (* Attention: could also came from proc_call ! ! *) +begin + QualifiedIdentifier; + VariableTail; +end; + +procedure TmwSimplePasPar.VariableTail; +begin + case TokenID of + ptRoundOpen: + begin + RoundOpen; + ExpressionList; + RoundClose; + end; + ptSquareOpen: + begin + IndexOp; + end; + ptPointerSymbol: + begin + PointerSymbol; + end; + ptLower: + begin + InitAhead; + AheadParse.NextToken; + AheadParse.TypeArgs; + + if AheadParse.TokenId = ptGreater then + begin + NextToken; + TypeArgs; + Expected(ptGreater); + case TokenID of + ptAddressOp, ptDoubleAddressOp, ptIdentifier: + begin + VariableReference; + end; + ptPoint, ptPointerSymbol, ptRoundOpen, ptSquareOpen: + begin + VariableTail; + end; + end; + end; + end; + end; + + case TokenID of + ptRoundOpen, ptSquareOpen, ptPointerSymbol: + begin + VariableTail; + end; + ptPoint: + begin + DotOp; + Variable; + end; + ptAs: + begin + AsOp; + SimpleExpression; + end; + end; +end; + +procedure TmwSimplePasPar.InterfaceType; +begin + case TokenID of + ptInterface: + begin + NextToken; + end; + ptDispInterface: + begin + NextToken; + end + else + begin + SynError(InvalidInterfaceType); + end; + end; + case TokenID of + ptEnd: + begin + NextToken; { Direct descendant without new members } + end; + ptRoundOpen: + begin + InterfaceHeritage; + case TokenID of + ptEnd: + begin + NextToken; { No new members } + end; + ptSemiColon: ; { No new members } + else + begin + if TokenID = ptSquareOpen then + begin + InterfaceGUID; + end; + InterfaceMemberList; + Expected(ptEnd); + end; + end; + end; + else + begin + if TokenID = ptSquareOpen then + begin + InterfaceGUID; + end; + InterfaceMemberList; { Direct descendant } + Expected(ptEnd); + end; + end; +end; + +procedure TmwSimplePasPar.InterfaceMemberList; +begin + while TokenID in [ptSquareOpen, ptFunction, ptProcedure, ptProperty] do + begin + while TokenID = ptSquareOpen do + CustomAttribute; + + ClassMethodOrProperty; + end; +end; + +procedure TmwSimplePasPar.ClassType; +begin + Expected(ptClass); + case TokenID of + ptIdentifier: //NASTY hack because Abstract is generally an ExID, except in this case when it should be a keyword. + begin + if Lexer.ExID = ptAbstract then + Expected(ptIdentifier); + + if Lexer.ExID = ptHelper then + ClassHelper; + end; + ptSealed: + Expected(ptSealed); + end; + case TokenID of + ptEnd: + begin + ClassTypeEnd; + NextToken; { Direct descendant of TObject without new members } + end; + ptRoundOpen: + begin + ClassHeritage; + case TokenID of + ptEnd: + begin + Expected(ptEnd); + ClassTypeEnd; + end; + ptSemiColon: ClassTypeEnd; + else + begin + ClassMemberList; { Direct descendant of TObject } + Expected(ptEnd); + ClassTypeEnd; + end; + end; + end; + ptSemicolon: ClassTypeEnd; + else + begin + ClassMemberList; { Direct descendant of TObject } + Expected(ptEnd); + ClassTypeEnd; + end; + end; +end; + +procedure TmwSimplePasPar.ClassHelper; +begin + ExpectedEx(ptHelper); + if TokenID = ptRoundOpen then + ClassHeritage; + Expected(ptFor); + TypeId; +end; + +procedure TmwSimplePasPar.ClassHeritage; +begin + Expected(ptRoundOpen); + AncestorIdList; + Expected(ptRoundClose); +end; + +procedure TmwSimplePasPar.ClassVisibility; +var + IsStrict: boolean; +begin + IsStrict := ExID = ptStrict; + if IsStrict then + ExpectedEx(ptStrict); + + while ExID in [ptAutomated, ptPrivate, ptProtected, ptPublic, ptPublished] do + begin + Lexer.InitAhead; + case Lexer.AheadExID of + ptColon, ptComma: ; + else + case ExID of + ptAutomated: + begin + VisibilityAutomated; + end; + ptPrivate: + begin + if IsStrict then + VisibilityStrictPrivate + else + VisibilityPrivate; + end; + ptProtected: + begin + if IsStrict then + VisibilityStrictProtected + else + VisibilityProtected; + end; + ptPublic: + begin + VisibilityPublic; + end; + ptPublished: + begin + VisibilityPublished; + end; + end; + end; + end; +end; + +procedure TmwSimplePasPar.VisibilityAutomated; +begin + ExpectedEx(ptAutomated); +end; + +procedure TmwSimplePasPar.VisibilityStrictPrivate; +begin + ExpectedEx(ptPrivate); +end; + +procedure TmwSimplePasPar.VisibilityPrivate; +begin + ExpectedEx(ptPrivate); +end; + +procedure TmwSimplePasPar.VisibilityStrictProtected; +begin + ExpectedEx(ptProtected); +end; + +procedure TmwSimplePasPar.VisibilityProtected; +begin + ExpectedEx(ptProtected); +end; + +procedure TmwSimplePasPar.VisibilityPublic; +begin + ExpectedEx(ptPublic); +end; + +procedure TmwSimplePasPar.VisibilityPublished; +begin + ExpectedEx(ptPublished); +end; + +procedure TmwSimplePasPar.VisibilityUnknown; +begin +end; + +procedure TmwSimplePasPar.ClassMemberList; +begin + while (TokenID in [ptClass, ptConstructor, ptDestructor, ptFunction, + ptIdentifier, ptProcedure, ptProperty, ptType, ptSquareOpen, ptVar, ptConst, ptCase]) or (ExID = ptStrict) do + begin + ClassVisibility; + + if TokenID = ptSquareOpen then + CustomAttribute; + + if (TokenID = ptIdentifier) and + not (ExID in [ptPrivate, ptProtected, ptPublished, ptPublic, ptStrict]) then + begin + InitAhead; + AheadParse.NextToken; + + if AheadParse.TokenId = ptEqual then + ConstantDeclaration + else + begin + ClassField; + if TokenID = ptEqual then + begin + NextToken; + TypedConstant; + end; + end; + + Semicolon; + end + else if TokenID in [ptClass, ptConstructor, ptDestructor, ptFunction, + ptProcedure, ptProperty, ptVar, ptConst] then + begin + ClassMethodOrProperty; + end; + if TokenID = ptType then + TypeSection; + if TokenID = ptCase then + begin + VariantSection; + end; + end; +end; + +procedure TmwSimplePasPar.ClassMethodOrProperty; +var + CurToken: TptTokenKind; +begin + if TokenID = ptClass then + begin + InitAhead; + AheadParse.NextToken; + CurToken := AheadParse.TokenID; + end else + CurToken := TokenID; + + case CurToken of + ptProperty: + begin + ClassProperty; + end; + ptVar, ptThreadVar: + begin + if TokenID = ptClass then + ClassClass; + + NextToken; + while (TokenID = ptIdentifier) and (ExID = ptUnknown) do + begin + ClassField; + Semicolon; + end; + end; + ptConst: + begin + if TokenID = ptClass then + ClassClass; + + NextToken; + while (TokenID = ptIdentifier) and (ExID = ptUnknown) do + begin + ConstantDeclaration; + Semicolon; + end; + end; + else + begin + ClassMethodHeading; + end; + end; +end; + +procedure TmwSimplePasPar.ClassProperty; +begin + if TokenID = ptClass then + ClassClass; + + Expected(ptProperty); + PropertyName; + case TokenID of + ptColon, ptSquareOpen: + begin + PropertyInterface; + end; + end; + PropertySpecifiers; + case ExID of + ptDefault: + begin + PropertyDefault; + Semicolon; + end; + end; +end; + +procedure TmwSimplePasPar.PropertyName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.ClassField; +begin + if TokenID = ptSquareOpen then + CustomAttribute; + FieldNameList; + Expected(ptColon); + TypeKind; + TypeDirective; +end; + +procedure TmwSimplePasPar.ObjectType; +begin + Expected(ptObject); + case TokenID of + ptEnd: + begin + ObjectTypeEnd; + NextToken; { Direct descendant without new members } + end; + ptRoundOpen: + begin + ObjectHeritage; + case TokenID of + ptEnd: + begin + Expected(ptEnd); + ObjectTypeEnd; + end; + ptSemiColon: ObjectTypeEnd; + else + begin + ObjectMemberList; { Direct descendant } + Expected(ptEnd); + ObjectTypeEnd; + end; + end; + end; + else + begin + ObjectMemberList; { Direct descendant } + Expected(ptEnd); + ObjectTypeEnd; + end; + end; +end; + +procedure TmwSimplePasPar.ObjectHeritage; +begin + Expected(ptRoundOpen); + AncestorIdList; + Expected(ptRoundClose); +end; + +procedure TmwSimplePasPar.ObjectMemberList; +begin {jdj added ptProperty-call to ObjectProperty 02/07/2001} + ObjectVisibility; + while TokenID in [ptConstructor, ptDestructor, ptFunction, ptIdentifier, + ptProcedure, ptProperty] do + begin + while TokenID = ptIdentifier do + begin + ObjectField; + Semicolon; + ObjectVisibility; + end; + while TokenID in [ptConstructor, ptDestructor, ptFunction, ptProcedure, ptProperty] do + begin + case TokenID of + ptConstructor, ptDestructor, ptFunction, ptProcedure: + ObjectMethodHeading; + ptProperty: + ObjectProperty; + end; + end; + ObjectVisibility; + end; +end; + +procedure TmwSimplePasPar.ObjectVisibility; +begin + while ExID in [ptPrivate, ptProtected, ptPublic] do + begin + Lexer.InitAhead; + case Lexer.AheadExID of + ptColon, ptComma: ; + else + case ExID of + ptPrivate: + begin + VisibilityPrivate; + end; + ptProtected: + begin + VisibilityProtected; + end; + ptPublic: + begin + VisibilityPublic; + end; + end; + end; + end; +end; + +procedure TmwSimplePasPar.ObjectField; +begin + IdentifierList; + Expected(ptColon); + TypeKind; + TypeDirective; +end; + +procedure TmwSimplePasPar.ClassReferenceType; +begin + Expected(ptClass); + Expected(ptOf); + TypeId; +end; + +procedure TmwSimplePasPar.VariantIdentifier; +begin + case ExID of + ptOleVariant: + begin + NextToken; + end; + ptVariant: + begin + NextToken; + end; + else + begin + SynError(InvalidVariantIdentifier); + end; + end; +end; + +procedure TmwSimplePasPar.ProceduralType; +var + TheTokenID: TptTokenKind; +begin + case TokenID of + ptFunction: + begin + NextToken; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + Expected(ptColon); + ReturnType; + end; + ptProcedure: + begin + NextToken; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + end; + else + begin + SynError(InvalidProceduralType); + end; + end; + if TokenID = ptOf then + ProceduralDirectiveOf; + + Lexer.InitAhead; + case TokenID of + ptSemiColon: TheTokenID := Lexer.AheadExID; + else + TheTokenID := ExID; + end; + while TheTokenID in [ptAbstract, ptCdecl, ptDynamic, ptExport, ptExternal, ptFar, + ptMessage, ptNear, ptOverload, ptOverride, ptPascal, ptRegister, + ptReintroduce, ptSafeCall, ptStdCall, ptVirtual, ptStatic, ptInline, ptVarargs, ptNoreturn] do + // DR 2001-11-14 no checking for deprecated etc. since it's captured by the typedecl + begin + if TokenID = ptSemiColon then Semicolon; + ProceduralDirective; + Lexer.InitAhead; + case TokenID of + ptSemiColon: TheTokenID := Lexer.AheadExID; + else + TheTokenID := ExID; + end; + end; + + if TokenID = ptOf then + ProceduralDirectiveOf; +end; + +procedure TmwSimplePasPar.StringConst; +begin + StringConstSimple; + while TokenID in [ptStringConst, ptAsciiChar] do + StringConstSimple; +end; + +procedure TmwSimplePasPar.StringConstSimple; +begin + NextToken; +end; + +procedure TmwSimplePasPar.StringIdentifier; +begin + case ExID of + ptAnsiString: + begin + NextToken; + end; + ptShortString: + begin + NextToken; + end; + ptWideString: + begin + NextToken; + end; + else + begin + SynError(InvalidStringIdentifier); + end; + end; +end; + +procedure TmwSimplePasPar.StringType; +begin + case TokenID of + ptString: + begin + NextToken; + if TokenID = ptSquareOpen then + begin + NextToken; + ConstantExpression; + Expected(ptSquareClose); + end; + end; + else + begin + VariableReference; + end; + end; +end; + +procedure TmwSimplePasPar.PointerSymbol; +begin + Expected(ptPointerSymbol); +end; + +procedure TmwSimplePasPar.PointerType; +begin + Expected(ptPointerSymbol); + TypeId; +end; + +procedure TmwSimplePasPar.StructuredType; +begin + case TokenID of + ptArray: + begin + ArrayType; + end; + ptFile: + begin + FileType; + end; + ptRecord: + begin + RecordType; + end; + ptSet: + begin + SetType; + end; + ptObject: + begin + ObjectType; + end + else + begin + SynError(InvalidStructuredType); + end; + end; +end; + +procedure TmwSimplePasPar.SimpleType; +begin + case TokenID of + ptMinus: + begin + NextToken; + end; + ptPlus: + begin + NextToken; + end; + end; + case FLexer.TokenID of + ptAsciiChar, ptIntegerConst: + begin + OrdinalType; + end; + ptFloat: + begin + RealType; + end; + ptIdentifier: + begin + InitAhead; + AheadParse.NextToken; + AheadParse.SimpleExpression; + if AheadParse.TokenID = ptDotDot then + SubrangeType + else + TypeId; + end; + else + begin + VariableReference; + end; + end; +end; + +procedure TmwSimplePasPar.RecordAlign; +begin + if ExID = ptAlign then + begin + NextToken; + RecordAlignValue; + end; +end; + +procedure TmwSimplePasPar.RecordAlignValue; +begin + Expected(ptIntegerConst); +end; + +procedure TmwSimplePasPar.RecordFieldConstant; +begin + Expected(ptIdentifier); + Expected(ptColon); + TypedConstant; +end; + +procedure TmwSimplePasPar.RecordConstant; +begin + Expected(ptRoundOpen); + RecordFieldConstant; + while (TokenID = ptSemiColon) do + begin + Semicolon; + if TokenId <> ptRoundClose then + RecordFieldConstant; + end; + Expected(ptRoundClose); +end; + +procedure TmwSimplePasPar.RecordConstraint; +begin + Expected(ptRecord); +end; + +procedure TmwSimplePasPar.ArrayConstant; +begin + Expected(ptRoundOpen); + + TypedConstant; + if TokenID = ptDotDot then + begin + NextToken; + TypedConstant; + end; + + while (TokenID = ptComma) do + begin + NextToken; + TypedConstant; + if TokenID = ptDotDot then + begin + NextToken; + TypedConstant; + end; + end; + Expected(ptRoundClose); +end; + +procedure TmwSimplePasPar.ArrayDimension; +begin + OrdinalType; +end; + +procedure TmwSimplePasPar.ClassForward; +begin + Expected(ptClass); +end; + +procedure TmwSimplePasPar.DispInterfaceForward; +begin + Expected(ptDispInterface); +end; + +procedure TmwSimplePasPar.DotOp; +begin + Expected(ptPoint); +end; + +procedure TmwSimplePasPar.InterfaceForward; +begin + Expected(ptInterface); +end; + +procedure TmwSimplePasPar.ObjectForward; +begin + Expected(ptObject); +end; + +procedure TmwSimplePasPar.TypeDeclaration; +begin + TypeName; + Expected(ptEqual); + + Lexer.InitAhead; + + if TokenID = ptType then + begin + if Lexer.AheadTokenID = ptOf then + begin + TypeReferenceType; + TypeDirective; + Exit; + end else + ExplicitType; + end; + + if (TokenID = ptPacked) and (Lexer.AheadTokenID in [ptClass, ptObject]) then + NextToken; + + case TokenID of + ptPointerSymbol: + begin + PointerType; + end; + ptClass: + begin + case Lexer.AheadTokenID of + ptOf: + begin + ClassReferenceType; + end; + ptSemiColon: + begin + ClassForward; + end; + else + begin + ClassType; + end; + end; + end; + ptInterface: + begin + case Lexer.AheadTokenID of + ptSemiColon: + begin + InterfaceForward; + end; + else + begin + InterfaceType; + end; + end; + end; + ptDispInterface: + begin + case Lexer.AheadTokenID of + ptSemiColon: + begin + DispInterfaceForward; + end; + else + begin + InterfaceType; + end; + end; + end; + ptObject: + begin + case Lexer.AheadTokenID of + ptSemiColon: + begin + ObjectForward; + end; + else + begin + ObjectType; + end; + end; + end; + else + begin + if ExID = ptReference then + AnonymousMethodType + else + TypeKind; + end; + end; + TypeDirective; +end; + +procedure TmwSimplePasPar.TypeName; +begin + Expected(ptIdentifier); + if TokenId = ptLower then + TypeParams; +end; + +procedure TmwSimplePasPar.ExplicitType; +begin + Expected(ptType); +end; + +procedure TmwSimplePasPar.TypeKind; +begin + case TokenID of + ptAsciiChar, ptFloat, ptIntegerConst, ptMinus, ptNil, ptPlus, ptStringConst, ptConst: + begin + SimpleType; + end; + ptRoundOpen: + begin + EnumeratedType; + end; + ptSquareOpen: + begin + SubrangeType; + end; + ptArray, ptFile, ptPacked, ptRecord, ptSet: + begin + if TokenID = ptPacked then + NextToken; + StructuredType; + end; + ptFunction, ptProcedure: + begin + ProceduralType; + end; + ptIdentifier: + begin + InitAhead; + AheadParse.NextToken; + AheadParse.SimpleExpression; + if AheadParse.TokenID = ptDotDot then + SubrangeType + else + TypeId; + end; + ptPointerSymbol: + begin + PointerType; + end; + ptString: + begin + TypeId; + end; + else + begin + SynError(InvalidTypeKind); + end; + end; +end; + +procedure TmwSimplePasPar.TypeArgs; +begin + TypeKind; + while TokenId = ptComma do + begin + NextToken; + TypeKind; + end; +end; + +procedure TmwSimplePasPar.TypedConstant; +var + RoundBrackets: Integer; +begin + case TokenID of + ptRoundOpen: + begin + Lexer.InitAhead; + while Lexer.AheadTokenID <> ptSemiColon do + case Lexer.AheadTokenID of + ptAnd, ptBegin, ptCase, ptColon, ptEnd, ptElse, ptIf, ptMinus, ptNull, + ptOr, ptPlus, ptShl, ptShr, ptSlash, ptStar, ptWhile, ptWith, + ptXor: Break; + ptRoundOpen: + begin + RoundBrackets := 0; + repeat + case Lexer.AheadTokenID of + ptBegin, ptCase, ptEnd, ptElse, ptIf, ptNull, ptWhile, ptWith: Break; + else + if Lexer.AheadTokenID = ptRoundOpen then + Inc(RoundBrackets); + if Lexer.AheadTokenID = ptRoundClose then + Dec(RoundBrackets); + + Lexer.AheadNext; + end; + until RoundBrackets = 0; + end; + else + Lexer.AheadNext; + end; + case Lexer.AheadTokenID of + ptColon: + begin + RecordConstant; + end; + ptNull: ; + ptAnd, ptMinus, ptOr, ptPlus, ptShl, ptShr, ptSlash, ptStar, ptXor: + begin + ConstantExpression; + end; + else + begin + ArrayConstant; + end; + end; + end; + ptSquareOpen: + ConstantExpression; + else + begin + ConstantExpression; + end; + end; +end; + +procedure TmwSimplePasPar.TypeId; +begin + TypeSimple; + + while TokenID = ptPoint do + begin + Expected(ptPoint); + TypeSimple; + end; + + if TokenID = ptRoundOpen then + begin + Expected(ptRoundOpen); + SimpleExpression; + Expected(ptRoundClose); + end; +end; + +procedure TmwSimplePasPar.ConstantExpression; +begin + SimpleExpression; +end; + +procedure TmwSimplePasPar.ResourceDeclaration; +begin + ConstantName; + Expected(ptEqual); + + ResourceValue; + + TypeDirective; +end; + +procedure TmwSimplePasPar.ResourceValue; +begin + CharString; + while TokenID = ptPlus do + begin + NextToken; + CharString; + end; +end; + +procedure TmwSimplePasPar.ConstantDeclaration; +begin + ConstantName; + case TokenID of + ptEqual: + begin + ConstantEqual; + end; + ptColon: + begin + ConstantColon; + end; + else + begin + SynError(InvalidConstantDeclaration); + end; + end; + TypeDirective; +end; + +procedure TmwSimplePasPar.ConstantColon; +begin + Expected(ptColon); + ConstantType; + Expected(ptEqual); + ConstantValueTyped; +end; + +procedure TmwSimplePasPar.ConstantEqual; +begin + Expected(ptEqual); + ConstantValue; +end; + +procedure TmwSimplePasPar.ConstantValue; +begin + Expression; +end; + +procedure TmwSimplePasPar.ConstantValueTyped; +begin + TypedConstant; +end; + +procedure TmwSimplePasPar.ConstantName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.ConstantType; +begin + TypeKind; +end; + +procedure TmwSimplePasPar.LabelId; +begin + case TokenID of + ptIntegerConst: + begin + NextToken; + end; + ptIdentifier: + begin + NextToken; + end; + else + begin + SynError(InvalidLabelId); + end; + end; +end; + +procedure TmwSimplePasPar.ProcedureDeclarationSection; +begin + if TokenID = ptClass then + begin + ClassMethod; + end; + case TokenID of + ptConstructor: + begin + ProcedureProcedureName; + end; + ptDestructor: + begin + ProcedureProcedureName; + end; + ptProcedure: + begin + ProcedureProcedureName; + end; + ptFunction: + begin + FunctionMethodDeclaration; + end; + ptIdentifier: + begin + if Lexer.ExID = ptOperator then + begin + FunctionMethodDeclaration; + end + else + SynError(InvalidProcedureDeclarationSection); + end; + else + begin + SynError(InvalidProcedureDeclarationSection); + end; + end; +end; + +procedure TmwSimplePasPar.LabelDeclarationSection; +begin + Expected(ptLabel); + LabelId; + while (TokenID = ptComma) do + begin + NextToken; + LabelId; + end; + Semicolon; +end; + +procedure TmwSimplePasPar.ProceduralDirective; +begin + case GenID of + ptAbstract: + begin + DirectiveBinding; + end; + ptCdecl, ptPascal, ptRegister, ptSafeCall, ptStdCall: + begin + DirectiveCalling; + end; + ptExport, ptFar, ptNear: + begin + Directive16Bit; + end; + ptExternal: + begin + ExternalDirective; + end; + ptDynamic, ptMessage, ptOverload, ptOverride, ptReintroduce, ptVirtual, ptNoreturn: + begin + DirectiveBinding; + end; + ptAssembler: + begin + NextToken; + end; + ptStatic: + begin + NextToken; + end; + ptInline: + begin + DirectiveInline; + end; + ptDeprecated: + DirectiveDeprecated; + ptLibrary: + DirectiveLibrary; + ptPlatform: + DirectivePlatform; + ptLocal: + DirectiveLocal; + ptVarargs: + DirectiveVarargs; + ptFinal, ptExperimental, ptDelayed: + NextToken; + else + begin + SynError(InvalidProceduralDirective); + end; + end; +end; + +procedure TmwSimplePasPar.ExportedHeading; +begin + case TokenID of + ptFunction: + begin + FunctionHeading; + end; + ptProcedure: + begin + ProcedureHeading; + end; + else + begin + SynError(InvalidExportedHeading); + end; + end; + if TokenID = ptSemiColon then Semicolon; + + //TODO: Add FINAL + while ExID in [ptAbstract, ptCdecl, ptDynamic, ptExport, ptExternal, ptFar, + ptMessage, ptNear, ptOverload, ptOverride, ptPascal, ptRegister, + ptReintroduce, ptSafeCall, ptStdCall, ptVirtual, + ptDeprecated, ptLibrary, ptPlatform, ptLocal, ptVarargs, + ptStatic, ptInline, ptAssembler, ptForward, ptDelayed, ptNoreturn] do + begin + case ExID of + ptAssembler: NextToken; + ptForward: ForwardDeclaration; + else + ProceduralDirective; + end; + if TokenID = ptSemiColon then Semicolon; + end; +end; + +procedure TmwSimplePasPar.FunctionHeading; +begin + Expected(ptFunction); + FunctionProcedureName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + if TokenID = ptColon then + begin + Expected(ptColon); + ReturnType; + end; +end; + +procedure TmwSimplePasPar.ProcedureHeading; +begin + Expected(ptProcedure); + FunctionProcedureName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; +end; + +procedure TmwSimplePasPar.VarSection; +begin + case TokenID of + ptThreadVar: + begin + NextToken; + end; + ptVar: + begin + NextToken; + end; + else + begin + SynError(InvalidVarSection); + end; + end; + while TokenID in [ptIdentifier, ptSquareOpen] do + begin + if TokenID = ptSquareOpen then + CustomAttribute + else + begin + VarDeclaration; + Semicolon; + end; + end; +end; + +procedure TmwSimplePasPar.TypeSection; +begin + Expected(ptType); + + while (TokenID = ptIdentifier) or (Lexer.TokenID = ptSquareOpen) do + begin + if TokenID = ptSquareOpen then + CustomAttribute + else + begin + InitAhead; + AheadParse.NextToken; + if AheadParse.TokenID = ptLower then + AheadParse.TypeParams; + + if AheadParse.TokenID <> ptEqual then + Break; + + TypeDeclaration; + if TokenID = ptEqual then + TypedConstant; + Semicolon; + end; + end; +end; + +procedure TmwSimplePasPar.TypeSimple; +begin + case GenID of + ptBoolean, ptByte, ptChar, ptDWord, ptInt64, ptInteger, ptLongInt, + ptLongWord, ptPChar, ptShortInt, ptSmallInt, ptWideChar, ptWord: + begin + OrdinalIdentifier; + end; + ptComp, ptCurrency, ptDouble, ptExtended, ptReal, ptReal48, ptSingle: + begin + RealIdentifier; + end; + ptAnsiString, ptShortString, ptWideString: + begin + StringIdentifier; + end; + ptOleVariant, ptVariant: + begin + VariantIdentifier; + end; + ptString: + begin + StringType; + end; + ptFile: + begin + FileType; + end; + ptArray: + begin + NextToken; + Expected(ptOf); + case TokenID of + ptConst: (*new in ObjectPascal80*) + begin + NextToken; + end; + else + begin + TypeID; + end; + end; + end; + else + Expected(ptIdentifier); + end; + + if TokenId = ptLower then + begin + Expected(ptLower); + TypeArgs; + Expected(ptGreater); + end; +end; + +procedure TmwSimplePasPar.TypeParamDecl; +begin + TypeParamList; + if TokenId = ptColon then + begin + NextToken; + ConstraintList; + end; +end; + +procedure TmwSimplePasPar.TypeParamDeclList; +begin + TypeParamDecl; + while TokenId = ptSemicolon do + begin + NextToken; + TypeParamDecl; + end; +end; + +procedure TmwSimplePasPar.TypeParamList; +begin + if TokenId = ptSquareOpen then + AttributeSection; + TypeSimple; + while TokenId = ptComma do + begin + NextToken; + if TokenId = ptSquareOpen then + AttributeSection; + TypeSimple; + end; +end; + +procedure TmwSimplePasPar.TypeParams; +begin + Expected(ptLower); + TypeParamDeclList; + // workaround for TSomeClass< T >= class(TObject) + if TokenID = ptGreaterEqual then + Lexer.RunPos := Lexer.RunPos - 1 + else + Expected(ptGreater); +end; + +procedure TmwSimplePasPar.TypeReferenceType; +begin + Expected(ptType); + Expected(ptOf); + TypeId; +end; + +procedure TmwSimplePasPar.ConstSection; +begin + case TokenID of + ptConst: + begin + NextToken; + while TokenID in [ptIdentifier, ptSquareOpen] do + begin + if TokenID = ptSquareOpen then + CustomAttribute + else + begin + ConstantDeclaration; + Semicolon; + end; + end; + end; + ptResourceString: + begin + NextToken; + while (TokenID = ptIdentifier) do + begin + ResourceDeclaration; + Semicolon; + end; + end + else + begin + SynError(InvalidConstSection); + end; + end; +end; + +procedure TmwSimplePasPar.InterfaceDeclaration; +begin + case TokenID of + ptConst: + begin + ConstSection; + end; + ptFunction: + begin + ExportedHeading; + end; + ptProcedure: + begin + ExportedHeading; + end; + ptResourceString: + begin + ConstSection; + end; + ptType: + begin + TypeSection; + end; + ptThreadVar: + begin + VarSection; + end; + ptVar: + begin + VarSection; + end; + ptExports: + begin + ExportsClause; + end; + ptSquareOpen: + begin + CustomAttribute; + end; + else + begin + SynError(InvalidInterfaceDeclaration); + end; + end; +end; + +procedure TmwSimplePasPar.ExportsElement; +begin + ExportsName; + if TokenID = ptRoundOpen then + begin + FormalParameterList; + end; + + if FLexer.ExID = ptIndex then + begin + NextToken; + Expected(ptIntegerConst); + end; + if FLexer.ExID = ptName then + begin + NextToken; + SimpleExpression; + end; + if FLexer.ExID = ptResident then + begin + NextToken; + end; +end; + +procedure TmwSimplePasPar.CompoundStatement; +begin + Expected(ptBegin); + Statements; + Expected(ptEnd); +end; + +procedure TmwSimplePasPar.ExportsClause; +begin + Expected(ptExports); + ExportsElement; + while TokenID = ptComma do + begin + NextToken; + ExportsElement; + end; + Semicolon; +end; + +procedure TmwSimplePasPar.ContainsClause; +begin + ExpectedEx(ptContains); + MainUsedUnitStatement; + while TokenID = ptComma do + begin + NextToken; + MainUsedUnitStatement; + end; + Semicolon; +end; + +procedure TmwSimplePasPar.RequiresClause; +begin + ExpectedEx(ptRequires); + RequiresIdentifier; + while TokenID = ptComma do + begin + NextToken; + RequiresIdentifier; + end; + Semicolon; +end; + +procedure TmwSimplePasPar.RequiresIdentifier; +begin + RequiresIdentifierId; + while Lexer.TokenID = ptPoint do + begin + NextToken; + RequiresIdentifierId; + end; +end; + +procedure TmwSimplePasPar.RequiresIdentifierId; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.InitializationSection; +begin + Expected(ptInitialization); + StatementList; +end; + +procedure TmwSimplePasPar.ImplementationSection; +begin + Expected(ptImplementation); + if TokenID = ptUses then + begin + UsesClause; + end; + while TokenID in [ptClass, ptConst, ptConstructor, ptDestructor, ptFunction, + ptLabel, ptProcedure, ptResourceString, ptThreadVar, ptType, ptVar, + ptExports, ptSquareOpen] do + begin + DeclarationSection; + end; +end; + +procedure TmwSimplePasPar.InterfaceSection; +begin + Expected(ptInterface); + if TokenID = ptUses then + begin + UsesClause; + end; + while TokenID in [ptConst, ptFunction, ptResourceString, ptProcedure, + ptThreadVar, ptType, ptVar, ptExports, ptSquareOpen] do + begin + InterfaceDeclaration; + end; +end; + +procedure TmwSimplePasPar.IdentifierList; +begin + Identifier; + while TokenID = ptComma do + begin + NextToken; + Identifier; + end; +end; + +procedure TmwSimplePasPar.CharString; +begin + case GenID of + ptAsciiChar, ptIdentifier, ptRoundOpen, ptStringConst: + while GenID in + [ptAsciiChar, ptIdentifier, ptRoundOpen, ptStringConst, ptString] do + begin + case TokenID of + ptIdentifier, ptRoundOpen: + begin + if ExID in [ptIndex] then + Break; + VariableReference; + end; + ptString: + begin + StringStatement; + Statement; + end; + else + StringConst; + end; +// if Lexer.TokenID = ptPoint then +// begin +// NextToken; +// VariableReference; +// end; + end; + else + begin + SynError(InvalidCharString); + end; + end; +end; + +procedure TmwSimplePasPar.IncludeFile; +begin + while TokenID <> ptNull do + case TokenID of + ptClass: + begin + ProcedureDeclarationSection; + end; + ptConst: + begin + ConstSection; + end; + ptConstructor: + begin + ProcedureDeclarationSection; + end; + ptDestructor: + begin + ProcedureDeclarationSection; + end; + ptExports: + begin + ExportsClause; + end; + ptFunction: + begin + ProcedureDeclarationSection; + end; + ptIdentifier: + begin + Statements; + end; + ptLabel: + begin + LabelDeclarationSection; + end; + ptProcedure: + begin + ProcedureDeclarationSection; + end; + ptResourceString: + begin + ConstSection; + end; + ptType: + begin + TypeSection; + end; + ptThreadVar: + begin + VarSection; + end; + ptVar: + begin + VarSection; + end; + else + begin + NextToken; + end; + end; +end; + +procedure TmwSimplePasPar.SkipSpace; +begin + Expected(ptSpace); + while TokenID in [ptSpace] do + Lexer.Next; +end; + +procedure TmwSimplePasPar.SkipCRLFco; +begin + Expected(ptCRLFCo); + while TokenID in [ptCRLFCo] do + Lexer.Next; +end; + +procedure TmwSimplePasPar.SkipCRLF; +begin + Expected(ptCRLF); + while TokenID in [ptCRLF] do + Lexer.Next; +end; + +procedure TmwSimplePasPar.ClassClass; +begin + Expected(ptClass); +end; + +procedure TmwSimplePasPar.ClassConstraint; +begin + Expected(ptClass); +end; + +procedure TmwSimplePasPar.PropertyDefault; +begin + ExpectedEx(ptDefault); +end; + +procedure TmwSimplePasPar.DispIDSpecifier; +begin + ExpectedEx(ptDispid); + ConstantExpression; +end; + +procedure TmwSimplePasPar.IndexOp; +begin + Expected(ptSquareOpen); + Expression; + while TokenID = ptComma do + begin + NextToken; + Expression; + end; + Expected(ptSquareClose); +end; + +procedure TmwSimplePasPar.IndexSpecifier; +begin + ExpectedEx(ptIndex); + ConstantExpression; +end; + +procedure TmwSimplePasPar.ClassTypeEnd; +begin + case ExID of + ptExperimental: NextToken; + ptDeprecated: DirectiveDeprecated; + end; +end; + +procedure TmwSimplePasPar.ObjectTypeEnd; +begin +end; + +procedure TmwSimplePasPar.DirectiveDeprecated; +begin + ExpectedEx(ptDeprecated); + if TokenID = ptStringConst then + NextToken; +end; + +procedure TmwSimplePasPar.DirectiveInline; +begin + Expected(ptInline); +end; + +procedure TmwSimplePasPar.DirectiveLibrary; +begin + Expected(ptLibrary); +end; + +procedure TmwSimplePasPar.DirectivePlatform; +begin + ExpectedEx(ptPlatform); +end; + +procedure TmwSimplePasPar.EnumeratedTypeItem; +begin + QualifiedIdentifier; + if TokenID = ptEqual then + begin + Expected(ptEqual); + ConstantExpression; + end; +end; + +procedure TmwSimplePasPar.Identifier; +begin + NextToken; +end; + +procedure TmwSimplePasPar.DirectiveLocal; +begin + ExpectedEx(ptLocal); +end; + +procedure TmwSimplePasPar.DirectiveVarargs; +begin + ExpectedEx(ptVarargs); +end; + +procedure TmwSimplePasPar.AncestorId; +begin + TypeId; +end; + +procedure TmwSimplePasPar.AncestorIdList; +begin + AncestorId; + while(TokenID = ptComma) do + begin + NextToken; + AncestorId; + end; +end; + +procedure TmwSimplePasPar.AnonymousMethod; +begin + case TokenID of + ptFunction: + begin + NextToken; + if TokenID = ptRoundOpen then + FormalParameterList; + Expected(ptColon); + ReturnType; + end; + ptProcedure: + begin + NextToken; + if TokenId = ptRoundOpen then + FormalParameterList; + end; + end; + ProceduralDirectiveList; + Block; +end; + +procedure TmwSimplePasPar.AnonymousMethodType; +begin + ExpectedEx(ptReference); + Expected(ptTo); + case TokenID of + ptProcedure: + begin + NextToken; + if TokenID = ptRoundOpen then + FormalParameterList; + end; + ptFunction: + begin + NextToken; + if TokenID = ptRoundOpen then + FormalParameterList; + Expected(ptColon); + ReturnType; + end; + end; + ProceduralDirectiveList; +end; + +procedure TmwSimplePasPar.AddDefine(const ADefine: string); +begin + FLexer.AddDefine(ADefine); +end; + +procedure TmwSimplePasPar.RemoveDefine(const ADefine: string); +begin + FLexer.RemoveDefine(ADefine); +end; + +function TmwSimplePasPar.IsDefined(const ADefine: string): Boolean; +begin + Result := FLexer.IsDefined(ADefine); +end; + +procedure TmwSimplePasPar.IsNotOp; +begin + Expected(ptIs); + Expected(ptNot); +end; + +procedure TmwSimplePasPar.ExportsNameId; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.ExportsName; +begin + ExportsNameId; + while FLexer.TokenID = ptPoint do + begin + NextToken; + ExportsNameId; + end; +end; + +procedure TmwSimplePasPar.ImplementsSpecifier; +begin + ExpectedEx(ptImplements); + + TypeId; + while (TokenID = ptComma) do + begin + NextToken; + TypeId; + end; +end; + +procedure TmwSimplePasPar.AttributeArgumentName; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.CaseLabelList; +begin + CaseLabel; + while TokenID = ptComma do + begin + NextToken; + CaseLabel; + end; +end; + +procedure TmwSimplePasPar.ArrayBounds; +begin + if TokenID = ptSquareOpen then + begin + NextToken; + ArrayDimension; + while TokenID = ptComma do + begin + NextToken; + ArrayDimension; + end; + Expected(ptSquareClose); + end; +end; + +procedure TmwSimplePasPar.DeclarationSections; +begin + while TokenID in [ptClass, ptConst, ptConstructor, ptDestructor, ptExports, ptFunction, ptLabel, ptProcedure, ptResourceString, ptThreadVar, ptType, ptVar, ptSquareOpen] do + begin + DeclarationSection; + end; +end; + +procedure TmwSimplePasPar.ProceduralDirectiveList; +begin + // A calling convention may follow a procedural signature without a leading + // semicolon, e.g. "reference to function(const P: T): HResult stdcall;" as + // used by Vcl.Edge.pas. Consume every such directive. + while GenID in [ptCdecl, ptPascal, ptRegister, ptSafeCall, ptStdCall, + ptVarargs, ptNoreturn] do + ProceduralDirective; +end; + +procedure TmwSimplePasPar.ProceduralDirectiveOf; +begin + NextToken; + Expected(ptObject); +end; + +procedure TmwSimplePasPar.TypeDirective; +begin + while GenID in [ptDeprecated, ptLibrary, ptPlatform, ptExperimental] do + case GenID of + ptDeprecated: DirectiveDeprecated; + ptLibrary: DirectiveLibrary; + ptPlatform: DirectivePlatform; + ptExperimental: NextToken; + end; +end; + +procedure TmwSimplePasPar.InheritedVariableReference; +begin + Expected(ptInherited); + if TokenID = ptIdentifier then + VariableReference; +end; + +procedure TmwSimplePasPar.ClearDefines; +begin + FLexer.ClearDefines; +end; + +procedure TmwSimplePasPar.InitAhead; +begin + if AheadParse = nil then + AheadParse := TmwSimplePasPar.Create; + AheadParse.Lexer.InitFrom(Lexer); +end; + +procedure TmwSimplePasPar.InitDefinesDefinedByCompiler; +begin + FLexer.InitDefinesDefinedByCompiler; +end; + +procedure TmwSimplePasPar.GlobalAttributes; +begin + GlobalAttributeSections; +end; + +procedure TmwSimplePasPar.GlobalAttributeSections; +begin + while TokenID = ptSquareOpen do + GlobalAttributeSection; +end; + +procedure TmwSimplePasPar.GlobalAttributeSection; +begin + Expected(ptSquareOpen); + GlobalAttributeTargetSpecifier; + AttributeList; + while TokenID = ptComma do + begin + Expected(ptComma); + GlobalAttributeTargetSpecifier; + AttributeList; + end; + Expected(ptSquareClose); +end; + +procedure TmwSimplePasPar.GlobalAttributeTargetSpecifier; +begin + GlobalAttributeTarget; + Expected(ptColon); +end; + +procedure TmwSimplePasPar.GlobalAttributeTarget; +begin + Expected(ptIdentifier); +end; + +procedure TmwSimplePasPar.Attributes; +begin + AttributeSections; +end; + +procedure TmwSimplePasPar.AttributeSections; +begin + while TokenID = ptSquareOpen do + AttributeSection; +end; + +procedure TmwSimplePasPar.AttributeSection; +begin + Expected(ptSquareOpen); + Lexer.InitAhead; + if Lexer.AheadTokenID = ptColon then + AttributeTargetSpecifier; + AttributeList; + while TokenID = ptComma do + begin + Lexer.InitAhead; + if Lexer.AheadTokenID = ptColon then + AttributeTargetSpecifier; + AttributeList; + end; + Expected(ptSquareClose); +end; + +procedure TmwSimplePasPar.AttributeTargetSpecifier; +begin + AttributeTarget; + Expected(ptColon); +end; + +procedure TmwSimplePasPar.AttributeTarget; +begin + case TokenID of + ptProperty: + Expected(ptProperty); + ptType: + Expected(ptType); + else + Expected(ptIdentifier); + end; +end; + +procedure TmwSimplePasPar.AttributeList; +begin + Attribute; + while TokenID = ptComma do + begin + Expected(ptComma); + AttributeList; + end; +end; + +procedure TmwSimplePasPar.Attribute; +begin + AttributeName; + if TokenID = ptRoundOpen then + AttributeArguments; +end; + +procedure TmwSimplePasPar.AttributeName; +begin + case TokenID of + ptIn, ptOut, ptConst, ptVar, ptUnsafe: + NextToken; + else + begin + Expected(ptIdentifier); + while TokenID = ptPoint do + begin + NextToken; + Expected(ptIdentifier); + end; + end; + end; +end; + +procedure TmwSimplePasPar.AttributeArguments; +begin + Expected(ptRoundOpen); + if TokenID <> ptRoundClose then + begin + Lexer.InitAhead; + if Lexer.AheadTokenID = ptEqual then + NamedArgumentList + else + PositionalArgumentList; + if Lexer.TokenID = ptEqual then + NamedArgumentList; + end; + Expected(ptRoundClose); +end; + +procedure TmwSimplePasPar.PositionalArgumentList; +begin + PositionalArgument; + while TokenID = ptComma do + begin + Expected(ptComma); + PositionalArgument; + end; +end; + +procedure TmwSimplePasPar.PositionalArgument; +begin + AttributeArgumentExpression; +end; + +procedure TmwSimplePasPar.NamedArgumentList; +begin + NamedArgument; + while TokenID = ptComma do + begin + Expected(ptComma); + NamedArgument; + end; +end; + +procedure TmwSimplePasPar.NamedArgument; +begin + AttributeArgumentName; + Expected(ptEqual); + AttributeArgumentExpression; +end; + +procedure TmwSimplePasPar.AttributeArgumentExpression; +begin + Expression; +end; + +procedure TmwSimplePasPar.CustomAttribute; +begin + //TODO: Global vs. Local attributes + AttributeSections; +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Source/SimpleParser/SimpleParser.rsj b/References/DelphiAST/Source/SimpleParser/SimpleParser.rsj new file mode 100644 index 000000000..a0502c6c7 --- /dev/null +++ b/References/DelphiAST/Source/SimpleParser/SimpleParser.rsj @@ -0,0 +1,4 @@ +{"version":1,"strings":[ +{"hash":24029655,"name":"simpleparser.rsexpected","value":"'%s' expected found '%s'"}, +{"hash":99256917,"name":"simpleparser.rsendoffile","value":"end of file"} +]} diff --git a/References/DelphiAST/Source/StringPool.pas b/References/DelphiAST/Source/StringPool.pas new file mode 100644 index 000000000..6489006fa --- /dev/null +++ b/References/DelphiAST/Source/StringPool.pas @@ -0,0 +1,118 @@ +unit StringPool; + +{$IFDEF FPC}{$MODE Delphi}{$ENDIF} + +interface + +type + TStringBucket = record + Hash: Cardinal; + Value: string; + end; + PStringBucket = ^TStringBucket; + TStringBuckets = array of TStringBucket; + + TStringPool = class + private + FBuckets: TStringBuckets; + FCount: Integer; + FGrowth: Integer; + FCapacity: Integer; + procedure Grow; + public + procedure StringIntern(var s: string); + + procedure Clear; + property Count: Integer read FCount; + end; + +implementation + +{ TStringPool } + +procedure TStringPool.Clear; +begin + SetLength(FBuckets, 0); + FCount := 0; + FGrowth := 0; + FCapacity := 0; +end; + +procedure TStringPool.Grow; +var + i, j, n: Integer; + oldBuckets: TStringBuckets; +begin + if FCapacity = 0 then + FCapacity := 32 + else + FCapacity := FCapacity * 2; + FGrowth := (FCapacity * 3) div 4 - FCount; + + oldBuckets := FBuckets; + FBuckets := nil; + SetLength(FBuckets, FCapacity); + + n := FCapacity - 1; + for i := 0 to High(oldBuckets) do + begin + if oldBuckets[i].Hash = 0 then + Continue; + j := oldBuckets[i].Hash and (FCapacity - 1); + while FBuckets[j].Hash <> 0 do + j := (j + 1) and n; + FBuckets[j].Hash := oldBuckets[i].Hash; + FBuckets[j].Value := oldBuckets[i].Value; + end; +end; + +procedure TStringPool.StringIntern(var s: string); + +{$OVERFLOWCHECKS OFF} + + function HashString(const s: string): Cardinal; inline; + var + i: Integer; + begin + // modified FNV-1a using length as seed + Result := Length(s); + for i := 1 to Result do + Result := (Result xor Ord(s[i])) * 16777619; + end; + +{$OVERFLOWCHECKS ON} + +var + hash: Cardinal; + i: Integer; + bucket: PStringBucket; +begin + if s = '' then + Exit; + + if FGrowth = 0 then + Grow; + + hash := HashString(s) shr 6; + i := hash and (FCapacity - 1); + + repeat + bucket := @FBuckets[i]; + if (bucket.Hash = hash) and (bucket.Value = s) then + begin + s := bucket.Value; + Exit; + end + else if bucket.Hash = 0 then + begin + bucket.Hash := hash; + bucket.Value := s; + Inc(FCount); + Dec(FGrowth); + Exit; + end; + i := (i + 1) and (FCapacity - 1); + until False; +end; + +end. diff --git a/References/DelphiAST/Test/DelphiASTTest.dpr b/References/DelphiAST/Test/DelphiASTTest.dpr new file mode 100644 index 000000000..0012281fb --- /dev/null +++ b/References/DelphiAST/Test/DelphiASTTest.dpr @@ -0,0 +1,16 @@ +program DelphiASTTest; + +uses + Vcl.Forms, + uMainForm in 'uMainForm.pas' {Form2}; + +{$R *.res} + +begin + System.ReportMemoryLeaksOnShutdown := True; + + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm2, Form2); + Application.Run; +end. diff --git a/References/DelphiAST/Test/DelphiASTTest.dproj b/References/DelphiAST/Test/DelphiASTTest.dproj new file mode 100644 index 000000000..4c07180ff --- /dev/null +++ b/References/DelphiAST/Test/DelphiASTTest.dproj @@ -0,0 +1,520 @@ +<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <PropertyGroup> + <ProjectGuid>{321657A2-2981-497B-80FB-95AE03110DDF}</ProjectGuid> + <ProjectVersion>17.2</ProjectVersion> + <FrameworkType>VCL</FrameworkType> + <MainSource>DelphiASTTest.dpr</MainSource> + <Base>True</Base> + <Config Condition="'$(Config)'==''">Debug</Config> + <Platform Condition="'$(Platform)'==''">Win32</Platform> + <TargetedPlatforms>1</TargetedPlatforms> + <AppType>Application</AppType> + </PropertyGroup> + <PropertyGroup Condition="'$(Config)'=='Base' or '$(Base)'!=''"> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Base)'=='true') or '$(Base_Win32)'!=''"> + <Base_Win32>true</Base_Win32> + <CfgParent>Base</CfgParent> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="('$(Platform)'=='Win64' and '$(Base)'=='true') or '$(Base_Win64)'!=''"> + <Base_Win64>true</Base_Win64> + <CfgParent>Base</CfgParent> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="'$(Config)'=='Debug' or '$(Cfg_1)'!=''"> + <Cfg_1>true</Cfg_1> + <CfgParent>Base</CfgParent> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="('$(Platform)'=='Win32' and '$(Cfg_1)'=='true') or '$(Cfg_1_Win32)'!=''"> + <Cfg_1_Win32>true</Cfg_1_Win32> + <CfgParent>Cfg_1</CfgParent> + <Cfg_1>true</Cfg_1> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="'$(Config)'=='Release' or '$(Cfg_2)'!=''"> + <Cfg_2>true</Cfg_2> + <CfgParent>Base</CfgParent> + <Base>true</Base> + </PropertyGroup> + <PropertyGroup Condition="'$(Base)'!=''"> + <VerInfo_Locale>1049</VerInfo_Locale> + <DCC_UnitSearchPath>..\Source;..\Source\SimpleParser;$(DCC_UnitSearchPath)</DCC_UnitSearchPath> + <SanitizedProjectName>DelphiASTTest</SanitizedProjectName> + <Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File> + <DCC_Namespace>System;Xml;Data;Datasnap;Web;Soap;Vcl;Vcl.Imaging;Vcl.Touch;Vcl.Samples;Vcl.Shell;$(DCC_Namespace)</DCC_Namespace> + <VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys> + <DCC_E>false</DCC_E> + <DCC_N>false</DCC_N> + <DCC_S>false</DCC_S> + <DCC_F>false</DCC_F> + <DCC_K>false</DCC_K> + </PropertyGroup> + <PropertyGroup Condition="'$(Base_Win32)'!=''"> + <VerInfo_Keys>CompanyName=;FileDescription=;FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProductName=;ProductVersion=1.0.0.0;Comments=</VerInfo_Keys> + <VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo> + <DCC_UsePackage>dxPScxSchedulerLnkRS21;JvGlobus;JvMM;cxSchedulerRibbonStyleEventEditorRS21;JvManagedThreads;cxSchedulerRS21;FireDACPgDriver;dxSkinOffice2007BlueRS21;RemObjects_Server_Indy_D21;FixInsight_XE7;JvCrypt;cxTreeListdxBarPopupMenuRS21;dxSkinHighContrastRS21;dxSkinSevenRS21;cxPivotGridRS21;DBXInterBaseDriver;DataSnapServer;DataSnapCommon;DataAbstract_SQLiteDriver_D21;DPIAwareExpert;JvNet;dxGaugeControlRS21;JvDotNetCtrls;cxEditorsRS21;DbxCommonDriver;CromisIPC;vclimg;dxTileControlRS21;dxSkinSilverRS21;dbxcds;cxPivotGridOLAPRS21;DatasnapConnectorsFreePascal;dxPSdxGaugeControlLnkRS21;dxPsPrVwAdvRS21;dxSkinMoneyTwinsRS21;JvXPCtrls;OmniXMLCore;vcldb;cxTreeListRS21;DataAbstract_DBXDriver_Enterprise_D21;GMLibEdit_DXE7;dxdborRS21;cxSpreadSheetRS21;dxBarExtItemsRS21;frxDBX21;dxWizardControlRS21;dxSkinBlueprintRS21;RemObjects_Synapse_D21;DataAbstract_SpiderMonkeyScripting_D21;CustomIPTransport;dsnap;IndyIPServer;dxSkinOffice2010BlackRS21;IndyCore;SynEdit_RXE7;dxSkinsdxBarPainterRS21;cxPageControldxBarPopupMenuRS21;dxSkinValentineRS21;CloudService;dxSkinDevExpressStyleRS21;FmxTeeUI;FireDACIBDriver;dxSkinCaramelRS21;dxPScxPCProdRS21;frxADO21;ShareBikeCommon_XE7;dxSkinOffice2013DarkGrayRS21;JvDB;JvRuntimeDesign;dxDockingRS21;dxLayoutControlRS21;dsnapxml;JclDeveloperTools;FireDACDb2Driver;dxSkinscxSchedulerPainterRS21;dxPSLnksRS21;dxPSdxDBOCLnkRS21;dxSkinVS2010RS21;cxLibraryRS21;bindcompfmx;cxDataRS21;dxComnRS21;FireDACODBCDriver;RESTBackendComponents;dxSkinBlackRS21;dxSkinDarkSideRS21;RemObjects_WebBroker_D21;dbrtl;FireDACCommon;bindcomp;inetdb;JvPluginSystem;dxPScxTLLnkRS21;DBXOdbcDriver;JvCmp;vclFireDAC;JvTimeFramework;xmlrtl;ibxpress;cxExportRS21;FireDACCommonDriver;dxSkinOffice2007PinkRS21;dxFlowChartRS21;bindengine;vclactnband;soaprtl;FMXTee;bindcompvcl;cxPageControlRS21;dxCoreRS21;Jcl;vclie;dxSkinOffice2007BlackRS21;dxPSCoreRS21;dxPSdxDBTVLnkRS21;dxPScxCommonRS21;dxADOServerModeRS21;FireDACMSSQLDriver;DBXInformixDriver;dxSkinLilianRS21;dxSkinWhiteprintRS21;DataSnapServerMidas;dxPSTeeChartRS21;DataAbstract_DBXDriver_Pro_D21;dsnapcon;DBXFirebirdDriver;dxNavBarRS21;inet;dxRibbonRS21;dxSkinsdxNavBarPainterRS21;JvPascalInterpreter;FireDACMySQLDriver;soapmidas;vclx;dxSkinOffice2013WhiteRS21;cxBarEditItemRS21;dxSkinsCoreRS21;DBXSybaseASADriver;dxFireDACServerModeRS21;dxSkinSharpPlusRS21;RESTComponents;dxSkinSevenClassicRS21;dbexpress;EurekaLogCore;IndyIPClient;dxThemeRS21;fsIBX21;FireDACSqliteDriver;dxSkinBlueRS21;FireDACDSDriver;dxDBXServerModeRS21;DBXSqliteDriver;dxSkinsdxDLPainterRS21;dxRichEditControlRS21;DPFiOSPackagesXE7;fmx;dxSkinMetropolisDarkRS21;cxVerticalGridRS21;IndySystem;dxSkinMetropolisRS21;TeeDB;tethering;dxSpreadSheetRS21;JvDlgs;dxSkinGlassOceansRS21;frxe21;vclib;dxSkinSummer2008RS21;DataSnapClient;dxPScxPivotGridLnkRS21;frxIBX21;frx21;DataSnapProviderClient;dxPSPrVwRibbonRS21;DBXSybaseASEDriver;ORM_R;cxGridRS21;RemObjects_Indy_D21;GMLib_DXE7;MetropolisUILiveTile;vcldsnap;dxSpellCheckerRS21;dxSkinLondonLiquidSkyRS21;dxSkinMcSkinRS21;dxSkinOffice2010SilverRS21;dxSkinOffice2007GreenRS21;fsTee21;fmxFireDAC;DBXDb2Driver;dxSkinFoggyRS21;DBXOracleDriver;JvCore;vclribbon;dxtrmdRS21;fmxase;vcl;dxBarExtDBItemsRS21;dxGDIPlusRS21;DBXMSSQLDriver;IndyIPCommon;CodeSiteExpressPkg;dxPSDBTeeChartRS21;dxSkinOffice2007SilverRS21;DataSnapFireDAC;FireDACDBXDriver;dxSkinStardustRS21;dxPSdxSpreadSheetLnkRS21;soapserver;JvAppFrm;dxdbtrRS21;inetdbxpress;FireDACInfxDriver;dxSkinCoffeeRS21;dxPSdxFCLnkRS21;dxPScxGridLnkRS21;FMXContainer_Runtime_XE7;JvDocking;adortl;RemObjects_Server_Synapse_D21;JvWizards;FireDACASADriver;JvHMI;fsADO21;JvBands;dxTabbedMDIRS21;emsclientfiredac;rtl;dxPScxSSLnkRS21;DbxClientDriver;dxSkinDarkRoomRS21;dxorgcRS21;dxPScxExtCommonRS21;dxPSdxOCLnkRS21;frxTee21;Tee;dxPSdxLCLnkRS21;JclContainers;frxDB21;dxMapControlRS21;JvSystem;DataSnapNativeClient;svnui;JvControls;dxSkinSpringTimeRS21;IndyProtocols;DBXMySQLDriver;cxPivotGridChartRS21;dxSkinOffice2013LightGrayRS21;dxSkinPumpkinRS21;bindcompdbx;TeeUI;fsDB21;JvJans;JvPrintPreview;JvPageComps;JvStdCtrls;cxSchedulerTreeBrowserRS21;dxmdsRS21;JvCustom;fs21;dxSkinDevExpressDarkStyleRS21;dxSkinSharpRS21;FireDACADSDriver;vcltouch;dxSkinscxPCPainterRS21;dxServerModeRS21;emsclient;FrameViewerXE7;dxSkinsdxRibbonPainterRS21;VCLRESTComponents;FireDAC;VclSmp;dxBarDBNavRS21;dxSkinTheAsphaltWorldRS21;dxSkinXmas2008BlueRS21;DataSnapConnectors;dxSkinLiquidSkyRS21;cxSchedulerGridRS21;fmxobj;JclVcl;dxPScxVGridLnkRS21;svn;dxBarRS21;FireDACOracleDriver;fmxdae;dxSkinOffice2010BlueRS21;VirtualTreesR;FireDACMSAccDriver;DataSnapIndy10ServerTransport;dxSkiniMaginaryRS21;$(DCC_UsePackage)</DCC_UsePackage> + <Manifest_File>$(BDS)\bin\default_app.manifest</Manifest_File> + <DCC_Namespace>Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace)</DCC_Namespace> + <VerInfo_Locale>1033</VerInfo_Locale> + </PropertyGroup> + <PropertyGroup Condition="'$(Base_Win64)'!=''"> + <DCC_UsePackage>dxPScxSchedulerLnkRS21;cxSchedulerRibbonStyleEventEditorRS21;cxSchedulerRS21;FireDACPgDriver;dxSkinOffice2007BlueRS21;RemObjects_Server_Indy_D21;cxTreeListdxBarPopupMenuRS21;dxSkinHighContrastRS21;dxSkinSevenRS21;cxPivotGridRS21;DBXInterBaseDriver;DataSnapServer;DataSnapCommon;DataAbstract_SQLiteDriver_D21;dxGaugeControlRS21;cxEditorsRS21;DbxCommonDriver;vclimg;dxTileControlRS21;dxSkinSilverRS21;dbxcds;cxPivotGridOLAPRS21;DatasnapConnectorsFreePascal;dxPSdxGaugeControlLnkRS21;dxPsPrVwAdvRS21;dxSkinMoneyTwinsRS21;vcldb;cxTreeListRS21;DataAbstract_DBXDriver_Enterprise_D21;dxdborRS21;cxSpreadSheetRS21;dxBarExtItemsRS21;dxWizardControlRS21;dxSkinBlueprintRS21;RemObjects_Synapse_D21;DataAbstract_SpiderMonkeyScripting_D21;CustomIPTransport;dsnap;IndyIPServer;dxSkinOffice2010BlackRS21;IndyCore;SynEdit_RXE7;dxSkinsdxBarPainterRS21;cxPageControldxBarPopupMenuRS21;dxSkinValentineRS21;CloudService;dxSkinDevExpressStyleRS21;FmxTeeUI;FireDACIBDriver;dxSkinCaramelRS21;dxPScxPCProdRS21;ShareBikeCommon_XE7;dxSkinOffice2013DarkGrayRS21;dxDockingRS21;dxLayoutControlRS21;dsnapxml;FireDACDb2Driver;dxSkinscxSchedulerPainterRS21;dxPSLnksRS21;dxPSdxDBOCLnkRS21;dxSkinVS2010RS21;cxLibraryRS21;bindcompfmx;cxDataRS21;dxComnRS21;FireDACODBCDriver;RESTBackendComponents;dxSkinBlackRS21;dxSkinDarkSideRS21;RemObjects_WebBroker_D21;dbrtl;FireDACCommon;bindcomp;inetdb;dxPScxTLLnkRS21;DBXOdbcDriver;vclFireDAC;xmlrtl;ibxpress;cxExportRS21;FireDACCommonDriver;dxSkinOffice2007PinkRS21;dxFlowChartRS21;bindengine;vclactnband;soaprtl;FMXTee;bindcompvcl;cxPageControlRS21;dxCoreRS21;vclie;dxSkinOffice2007BlackRS21;dxPSCoreRS21;dxPSdxDBTVLnkRS21;dxPScxCommonRS21;dxADOServerModeRS21;FireDACMSSQLDriver;DBXInformixDriver;dxSkinLilianRS21;dxSkinWhiteprintRS21;DataSnapServerMidas;dxPSTeeChartRS21;DataAbstract_DBXDriver_Pro_D21;dsnapcon;DBXFirebirdDriver;dxNavBarRS21;inet;dxRibbonRS21;dxSkinsdxNavBarPainterRS21;FireDACMySQLDriver;soapmidas;vclx;dxSkinOffice2013WhiteRS21;cxBarEditItemRS21;dxSkinsCoreRS21;DBXSybaseASADriver;dxFireDACServerModeRS21;dxSkinSharpPlusRS21;RESTComponents;dxSkinSevenClassicRS21;dbexpress;IndyIPClient;dxThemeRS21;FireDACSqliteDriver;dxSkinBlueRS21;FireDACDSDriver;dxDBXServerModeRS21;DBXSqliteDriver;dxSkinsdxDLPainterRS21;dxRichEditControlRS21;fmx;dxSkinMetropolisDarkRS21;cxVerticalGridRS21;IndySystem;dxSkinMetropolisRS21;TeeDB;tethering;dxSpreadSheetRS21;dxSkinGlassOceansRS21;vclib;dxSkinSummer2008RS21;DataSnapClient;dxPScxPivotGridLnkRS21;DataSnapProviderClient;dxPSPrVwRibbonRS21;DBXSybaseASEDriver;cxGridRS21;RemObjects_Indy_D21;GMLib_DXE7;MetropolisUILiveTile;vcldsnap;dxSpellCheckerRS21;dxSkinLondonLiquidSkyRS21;dxSkinMcSkinRS21;dxSkinOffice2010SilverRS21;dxSkinOffice2007GreenRS21;fmxFireDAC;DBXDb2Driver;dxSkinFoggyRS21;DBXOracleDriver;vclribbon;dxtrmdRS21;fmxase;vcl;dxBarExtDBItemsRS21;dxGDIPlusRS21;DBXMSSQLDriver;IndyIPCommon;dxPSDBTeeChartRS21;dxSkinOffice2007SilverRS21;DataSnapFireDAC;FireDACDBXDriver;dxSkinStardustRS21;dxPSdxSpreadSheetLnkRS21;soapserver;dxdbtrRS21;inetdbxpress;FireDACInfxDriver;dxSkinCoffeeRS21;dxPSdxFCLnkRS21;dxPScxGridLnkRS21;adortl;RemObjects_Server_Synapse_D21;FireDACASADriver;dxTabbedMDIRS21;emsclientfiredac;rtl;dxPScxSSLnkRS21;DbxClientDriver;dxSkinDarkRoomRS21;dxorgcRS21;dxPScxExtCommonRS21;dxPSdxOCLnkRS21;Tee;dxPSdxLCLnkRS21;dxMapControlRS21;DataSnapNativeClient;dxSkinSpringTimeRS21;IndyProtocols;DBXMySQLDriver;cxPivotGridChartRS21;dxSkinOffice2013LightGrayRS21;dxSkinPumpkinRS21;bindcompdbx;TeeUI;cxSchedulerTreeBrowserRS21;dxmdsRS21;dxSkinDevExpressDarkStyleRS21;dxSkinSharpRS21;FireDACADSDriver;vcltouch;dxSkinscxPCPainterRS21;dxServerModeRS21;emsclient;dxSkinsdxRibbonPainterRS21;VCLRESTComponents;FireDAC;VclSmp;dxBarDBNavRS21;dxSkinTheAsphaltWorldRS21;dxSkinXmas2008BlueRS21;DataSnapConnectors;dxSkinLiquidSkyRS21;cxSchedulerGridRS21;fmxobj;dxPScxVGridLnkRS21;dxBarRS21;FireDACOracleDriver;fmxdae;dxSkinOffice2010BlueRS21;VirtualTreesR;FireDACMSAccDriver;DataSnapIndy10ServerTransport;dxSkiniMaginaryRS21;$(DCC_UsePackage)</DCC_UsePackage> + </PropertyGroup> + <PropertyGroup Condition="'$(Cfg_1)'!=''"> + <DCC_Define>DEBUG;$(DCC_Define)</DCC_Define> + <DCC_DebugDCUs>true</DCC_DebugDCUs> + <DCC_Optimize>false</DCC_Optimize> + <DCC_GenerateStackFrames>true</DCC_GenerateStackFrames> + <DCC_DebugInfoInExe>true</DCC_DebugInfoInExe> + <DCC_RemoteDebug>true</DCC_RemoteDebug> + </PropertyGroup> + <PropertyGroup Condition="'$(Cfg_1_Win32)'!=''"> + <VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo> + <VerInfo_Locale>1033</VerInfo_Locale> + <DCC_RemoteDebug>false</DCC_RemoteDebug> + </PropertyGroup> + <PropertyGroup Condition="'$(Cfg_2)'!=''"> + <DCC_LocalDebugSymbols>false</DCC_LocalDebugSymbols> + <DCC_Define>RELEASE;$(DCC_Define)</DCC_Define> + <DCC_SymbolReferenceInfo>0</DCC_SymbolReferenceInfo> + <DCC_DebugInformation>0</DCC_DebugInformation> + </PropertyGroup> + <ItemGroup> + <DelphiCompile Include="$(MainSource)"> + <MainSource>MainSource</MainSource> + </DelphiCompile> + <DCCReference Include="uMainForm.pas"> + <Form>Form2</Form> + <FormType>dfm</FormType> + </DCCReference> + <BuildConfiguration Include="Release"> + <Key>Cfg_2</Key> + <CfgParent>Base</CfgParent> + </BuildConfiguration> + <BuildConfiguration Include="Base"> + <Key>Base</Key> + </BuildConfiguration> + <BuildConfiguration Include="Debug"> + <Key>Cfg_1</Key> + <CfgParent>Base</CfgParent> + </BuildConfiguration> + </ItemGroup> + <ProjectExtensions> + <Borland.Personality>Delphi.Personality.12</Borland.Personality> + <Borland.ProjectType>Application</Borland.ProjectType> + <BorlandProject> + <Delphi.Personality> + <Source> + <Source Name="MainSource">DelphiASTTest.dpr</Source> + </Source> + <Excluded_Packages> + <Excluded_Packages Name="C:\Users\Public\Documents\Embarcadero\Studio\15.0\Bpl\FixInsightIntegration.bpl">(untitled)</Excluded_Packages> + <Excluded_Packages Name="$(BDSBIN)\dcloffice2k210.bpl">Microsoft Office 2000 Sample Automation Server Wrapper Components</Excluded_Packages> + <Excluded_Packages Name="$(BDSBIN)\dclofficexp210.bpl">Microsoft Office XP Sample Automation Server Wrapper Components</Excluded_Packages> + </Excluded_Packages> + </Delphi.Personality> + <Deployment Version="1"> + <DeployFile LocalName="Win32\Debug\DelphiASTTest.exe" Configuration="Debug" Class="ProjectOutput"> + <Platform Name="Win32"> + <RemoteName>DelphiASTTest.exe</RemoteName> + <Overwrite>true</Overwrite> + </Platform> + </DeployFile> + <DeployClass Required="true" Name="DependencyPackage"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + <Platform Name="Win32"> + <Operation>0</Operation> + <Extensions>.bpl</Extensions> + </Platform> + <Platform Name="OSX32"> + <RemoteDir>Contents\MacOS</RemoteDir> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + </DeployClass> + <DeployClass Name="DependencyModule"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + <Platform Name="Win32"> + <Operation>0</Operation> + <Extensions>.dll;.bpl</Extensions> + </Platform> + <Platform Name="OSX32"> + <RemoteDir>Contents\MacOS</RemoteDir> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + <Extensions>.dylib</Extensions> + </Platform> + </DeployClass> + <DeployClass Name="iPad_Launch2048"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectOSXInfoPList"> + <Platform Name="OSX32"> + <RemoteDir>Contents</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectiOSDeviceDebug"> + <Platform Name="iOSDevice64"> + <RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <RemoteDir>..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_SplashImage470"> + <Platform Name="Android"> + <RemoteDir>res\drawable-normal</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidLibnativeX86File"> + <Platform Name="Android"> + <RemoteDir>library\lib\x86</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectiOSResource"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectOSXEntitlements"> + <Platform Name="OSX32"> + <RemoteDir>../</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidGDBServer"> + <Platform Name="Android"> + <RemoteDir>library\lib\armeabi-v7a</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPhone_Launch640"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_SplashImage960"> + <Platform Name="Android"> + <RemoteDir>res\drawable-xlarge</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_LauncherIcon96"> + <Platform Name="Android"> + <RemoteDir>res\drawable-xhdpi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPhone_Launch320"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_LauncherIcon144"> + <Platform Name="Android"> + <RemoteDir>res\drawable-xxhdpi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidLibnativeMipsFile"> + <Platform Name="Android"> + <RemoteDir>library\lib\mips</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidSplashImageDef"> + <Platform Name="Android"> + <RemoteDir>res\drawable</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="DebugSymbols"> + <Platform Name="OSX32"> + <RemoteDir>Contents\MacOS</RemoteDir> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="Win32"> + <Operation>0</Operation> + </Platform> + </DeployClass> + <DeployClass Name="DependencyFramework"> + <Platform Name="OSX32"> + <RemoteDir>Contents\MacOS</RemoteDir> + <Operation>1</Operation> + <Extensions>.framework</Extensions> + </Platform> + <Platform Name="Win32"> + <Operation>0</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_SplashImage426"> + <Platform Name="Android"> + <RemoteDir>res\drawable-small</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectiOSEntitlements"> + <Platform Name="iOSDevice64"> + <RemoteDir>../</RemoteDir> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <RemoteDir>../</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AdditionalDebugSymbols"> + <Platform Name="OSX32"> + <RemoteDir>Contents\MacOS</RemoteDir> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="Win32"> + <RemoteDir>Contents\MacOS</RemoteDir> + <Operation>0</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidClassesDexFile"> + <Platform Name="Android"> + <RemoteDir>classes</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectiOSInfoPList"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPad_Launch1024"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_DefaultAppIcon"> + <Platform Name="Android"> + <RemoteDir>res\drawable</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectOSXResource"> + <Platform Name="OSX32"> + <RemoteDir>Contents\Resources</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectiOSDeviceResourceRules"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPad_Launch768"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Required="true" Name="ProjectOutput"> + <Platform Name="Android"> + <RemoteDir>library\lib\armeabi-v7a</RemoteDir> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="Win32"> + <Operation>0</Operation> + </Platform> + <Platform Name="OSX32"> + <RemoteDir>Contents\MacOS</RemoteDir> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidLibnativeArmeabiFile"> + <Platform Name="Android"> + <RemoteDir>library\lib\armeabi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_SplashImage640"> + <Platform Name="Android"> + <RemoteDir>res\drawable-large</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="File"> + <Platform Name="Android"> + <Operation>0</Operation> + </Platform> + <Platform Name="iOSDevice64"> + <Operation>0</Operation> + </Platform> + <Platform Name="Win32"> + <Operation>0</Operation> + </Platform> + <Platform Name="OSX32"> + <RemoteDir>Contents\MacOS</RemoteDir> + <Operation>0</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>0</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>0</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPhone_Launch640x1136"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_LauncherIcon36"> + <Platform Name="Android"> + <RemoteDir>res\drawable-ldpi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="AndroidSplashStyles"> + <Platform Name="Android"> + <RemoteDir>res\values</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="iPad_Launch1536"> + <Platform Name="iOSDevice64"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSSimulator"> + <Operation>1</Operation> + </Platform> + <Platform Name="iOSDevice32"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_LauncherIcon48"> + <Platform Name="Android"> + <RemoteDir>res\drawable-mdpi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="Android_LauncherIcon72"> + <Platform Name="Android"> + <RemoteDir>res\drawable-hdpi</RemoteDir> + <Operation>1</Operation> + </Platform> + </DeployClass> + <DeployClass Name="ProjectAndroidManifest"> + <Platform Name="Android"> + <Operation>1</Operation> + </Platform> + </DeployClass> + <ProjectRoot Platform="iOSDevice32" Name="$(PROJECTNAME).app"/> + <ProjectRoot Platform="Android" Name="$(PROJECTNAME)"/> + <ProjectRoot Platform="Win32" Name="$(PROJECTNAME)"/> + <ProjectRoot Platform="iOSDevice64" Name="$(PROJECTNAME).app"/> + <ProjectRoot Platform="OSX32" Name="$(PROJECTNAME).app"/> + <ProjectRoot Platform="iOSSimulator" Name="$(PROJECTNAME).app"/> + <ProjectRoot Platform="Win64" Name="$(PROJECTNAME)"/> + </Deployment> + <Platforms> + <Platform value="Win32">True</Platform> + <Platform value="Win64">False</Platform> + </Platforms> + </BorlandProject> + <ProjectFileVersion>12</ProjectFileVersion> + </ProjectExtensions> + <Import Project="$(BDS)\Bin\CodeGear.Delphi.Targets" Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')"/> + <Import Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj" Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')"/> + <Import Project="$(MSBuildProjectName).deployproj" Condition="Exists('$(MSBuildProjectName).deployproj')"/> +</Project> diff --git a/References/DelphiAST/Test/DelphiASTTest.lpr b/References/DelphiAST/Test/DelphiASTTest.lpr new file mode 100644 index 000000000..30483af94 --- /dev/null +++ b/References/DelphiAST/Test/DelphiASTTest.lpr @@ -0,0 +1,16 @@ +program DelphiASTTest; + +{$MODE Delphi} + +uses + Forms, Interfaces, + uMainForm in 'uMainForm.pas' {Form2}; + +{$R *.res} + +begin + Application.Initialize; + Application.MainFormOnTaskbar := True; + Application.CreateForm(TForm2, Form2); + Application.Run; +end. diff --git a/References/DelphiAST/Test/Snippets/DeprecatedOnConst.pas b/References/DelphiAST/Test/Snippets/DeprecatedOnConst.pas new file mode 100644 index 000000000..8d27ffc55 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/DeprecatedOnConst.pas @@ -0,0 +1,11 @@ +unit DeprecatedOnConst; + +interface + +const + MyConst = 'test' deprecated 'Do not use'; + MyConst2 = 'test2' platform; + MyConst3 = 'test4' library; + +implementation +end. diff --git a/References/DelphiAST/Test/Snippets/VariantRecordFieldAttributes.pas b/References/DelphiAST/Test/Snippets/VariantRecordFieldAttributes.pas new file mode 100644 index 000000000..bbe6a275b --- /dev/null +++ b/References/DelphiAST/Test/Snippets/VariantRecordFieldAttributes.pas @@ -0,0 +1,17 @@ +unit VariantRecordFieldAttributes; + +interface + +type + TVariantRecord = record + case byte of + 1:( + Value: Double; + [Example] + ValueWithAttribute: Integer; + ); + end; + +implementation + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/alignedrecords.pas b/References/DelphiAST/Test/Snippets/alignedrecords.pas new file mode 100644 index 000000000..659e57ac7 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/alignedrecords.pas @@ -0,0 +1,26 @@ +unit alignedrecords; + +interface + +type + TMyRecord = record + Value: Integer; + Align: string; + class operator Initialize (out Dest: TMyRecord); + class operator Finalize(var Dest: TMyRecord); + end align 8; + +implementation + +class operator TMyRecord.Initialize (out Dest: TMyRecord); +begin + Dest.Value := 10; + Log('created' + IntToHex (Integer(Pointer(@Dest)))); +end; + +class operator TMyRecord.Finalize(var Dest: TMyRecord); +begin + Log('destroyed' + IntToHex (Integer(Pointer(@Dest)))); +end; + +end. diff --git a/References/DelphiAST/Test/Snippets/constset.pas b/References/DelphiAST/Test/Snippets/constset.pas new file mode 100644 index 000000000..3d983387e --- /dev/null +++ b/References/DelphiAST/Test/Snippets/constset.pas @@ -0,0 +1,21 @@ +unit constset; + +interface + +type + TClass = class + public type + TInnerEnum = (eOne, eTwo, wThree); + end; + +const + cConstant: set of TClass.TInnerEnum = [ + TClass.TInnerEnum.eOne, + TClass.TInnerEnum.eTwo, + TClass.TInnerEnum.wThree + ]; + + +implementation + +end. diff --git a/References/DelphiAST/Test/Snippets/deprecatedtype.pas b/References/DelphiAST/Test/Snippets/deprecatedtype.pas new file mode 100644 index 000000000..d9b3f9464 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/deprecatedtype.pas @@ -0,0 +1,20 @@ +unit deprecatedtype deprecated; + +interface + +type + TFoo = record + end deprecated 'Use TBar'; + + TBar = record + end deprecated; + + TFooClass = class + end deprecated 'Use TBarClass'; + + TBarClass = class + end deprecated; + +implementation + +end. diff --git a/References/DelphiAST/Test/Snippets/dottedtypes.pas b/References/DelphiAST/Test/Snippets/dottedtypes.pas new file mode 100644 index 000000000..148b405b4 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/dottedtypes.pas @@ -0,0 +1,37 @@ +unit dottedtypes; + +interface + +uses + MyUnit; + +type + TSample<T: MyUnit.TItem, MyUnit.MyType.IStuff> = class(MyUnit.TBaseClass, MyUnit.IStuff) + public + function DoStuff<T2: MyUnit.TMyObject>(Obj: MyUnit.TMyObject): MyUnit.TMyObject; + + property Obj : TObj read FObj implements MyUnit.IStuff; + end; + +implementation + +function TSample<T>.DoStuff<T2>(Obj: MyUnit.TMyObject): MyUnit.TMyObject; +var + Obj2: MyUnit.TMyObject; + Obj3, Obj4: MyUnit.TMyAdditionalObject; + Sample: TSample<MyUnit.TSpecialItem>; + MyObjectArray: array of MyUnit.TMyObject; +begin + Sample := TSample<MyUnit.TSpecialItem>.Create; + + try + Sample.DoOtherStuff<MyUnit.TSpecialObject, MyUnit.TObject>(Obj2); + except + on E: MyUnit.MyException do + begin + WriteLn(E.Message); + end; + end; +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/endtoken.pas b/References/DelphiAST/Test/Snippets/endtoken.pas new file mode 100644 index 000000000..2a94359b4 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/endtoken.pas @@ -0,0 +1,32 @@ +unit endtoken; + +interface + +function BitsHighest(X: Cardinal): Integer; + +implementation + +// Bit manipulation +function BitsHighest(X: Cardinal): Integer; +asm + {$IFDEF CPU32} + // --> EAX X + // <-- EAX + MOV ECX, EAX + MOV EAX, -1 + BSR EAX, ECX + JNZ @@End + MOV EAX, -1 +@@End: + {$ENDIF CPU32} + {$IFDEF CPU64} + // --> ECX X + // <-- RAX + MOV EAX, -1 + MOV R10D, EAX + BSR EAX, ECX + CMOVZ EAX, R10D + {$ENDIF CPU64} +end; + +end. diff --git a/References/DelphiAST/Test/Snippets/experimentals.pas b/References/DelphiAST/Test/Snippets/experimentals.pas new file mode 100644 index 000000000..65c1ce643 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/experimentals.pas @@ -0,0 +1,18 @@ +unit experimentals experimental; + +interface + +type + TExperimentalClass = class + Field: Integer; + end experimental; + +procedure someExperimentalProc(); + +implementation + +procedure someExperimentalProc(); experimental; +begin +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/externalfunction.pas b/References/DelphiAST/Test/Snippets/externalfunction.pas new file mode 100644 index 000000000..dcca0c8dd --- /dev/null +++ b/References/DelphiAST/Test/Snippets/externalfunction.pas @@ -0,0 +1,9 @@ +unit externalfunction; + +interface + +function CreateJobObjectA; external Kernel32 name 'CreateJobObjectA'; + +implementation + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/finalizationinitializationexports.pas b/References/DelphiAST/Test/Snippets/finalizationinitializationexports.pas new file mode 100644 index 000000000..9c755dc8a --- /dev/null +++ b/References/DelphiAST/Test/Snippets/finalizationinitializationexports.pas @@ -0,0 +1,53 @@ +unit finalizationinitializationexports; + +interface + +type + TFoo = class(TObject) + function A : Integer; + constructor Create; + end; + + TBar = record + procedure B; + end; + + IFooBar = interface + ['{BED74FE6-570B-40F8-ABF0-5E23C8EE8E7E}'] + procedure C; + end; + + procedure Hello; + +const + A = 1; + +implementation + +procedure Hello; +begin + +end; + +{ TFoo } + +function TFoo.A : Integer; +begin + +end; + +constructor TFoo.Create; +begin + +end; + +exports + Hello; + +initialization + Hello; + +finalization + Hello; + +end. diff --git a/References/DelphiAST/Test/Snippets/forwardoverloaded.pas b/References/DelphiAST/Test/Snippets/forwardoverloaded.pas new file mode 100644 index 000000000..8c5381938 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/forwardoverloaded.pas @@ -0,0 +1,19 @@ +unit forwardoverloaded; + +interface + +implementation + +procedure Test; forward; overload; + +procedure Test(AParam: Integer); overload; +begin + +end; + +procedure Test; +begin + +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/forwardwithoutsemicolon.pas b/References/DelphiAST/Test/Snippets/forwardwithoutsemicolon.pas new file mode 100644 index 000000000..712c8f3ff --- /dev/null +++ b/References/DelphiAST/Test/Snippets/forwardwithoutsemicolon.pas @@ -0,0 +1,20 @@ +unit forwardwithoutsemicolon; + +interface + +procedure proc1(); forward // NO TRAILING SEMICOLON +procedure proc2(); + +implementation + +procedure proc1(); +begin + +end; + +procedure proc2(); +begin + +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/genericconstraints.pas b/References/DelphiAST/Test/Snippets/genericconstraints.pas new file mode 100644 index 000000000..37c3ac9ce --- /dev/null +++ b/References/DelphiAST/Test/Snippets/genericconstraints.pas @@ -0,0 +1,26 @@ +unit genericconstraints; + +interface + +type + TFoo<T: TComponent> = class(TObject); // T inherits from TComponent + TFoo<TComma1, TComma2> = class(TObject); // TComma1 and TComma2 have no constraints + TBar<TTwoConstraints: TComponent, IUnknown> = class(TObject); // TTwoConstraints inherits from TComponent and implements IUnknown + TBar<T1: TComponent; T2: IUnknown> = class(TObject); // T1 inherits from TComponent, T2 implements IUnknown + TBaz<TThreeConstraints: TComponent, IUnknown, constructor> = class(TObject); // TThreeConstraints inherits from TComponent, implements IUnknown and has a constructor without parameters + TBaz<TComma1, TComma2: class> = class(TObject); // TComma1 and TComma2 are classes + TBax<T1: class; T2: record> = class(TObject) // T1 is a class, T2 is a record + public + procedure DoStuff; + end; + +implementation + +{ TBax<T1, T2> } + +procedure TBax<T1, T2>.DoStuff; +begin +// +end; + +end. diff --git a/References/DelphiAST/Test/Snippets/genericinterfacemethoddelegation.pas b/References/DelphiAST/Test/Snippets/genericinterfacemethoddelegation.pas new file mode 100644 index 000000000..4fe0efc68 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/genericinterfacemethoddelegation.pas @@ -0,0 +1,28 @@ +unit genericinterfacemethoddelegation; + +interface + +uses + SysUtils; + +type + TGenerator<T1, TResult> = class(TInterfacedObject, + TFunc<T1, IEnumerable<TResult>>) + private + function TFunc<T1, IEnumerable<TResult>>.Invoke = Bind; + public + constructor Create(const proc: TProc<T1>); + function Bind(arg1: T1): IEnumerable<TResult>; + end; + +implementation + +function TGenerator<T1, TResult>.Bind(arg1: T1): IEnumerable<TResult>; +begin +end; + +constructor TGenerator<T1, TResult>.Create(const proc: TProc<T1>); +begin +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/implementsgenerictype.pas b/References/DelphiAST/Test/Snippets/implementsgenerictype.pas new file mode 100644 index 000000000..69dbd0afc --- /dev/null +++ b/References/DelphiAST/Test/Snippets/implementsgenerictype.pas @@ -0,0 +1,18 @@ +unit implementsgenerictype; + +interface + +type + IFoo<T> = interface + end; + + TBar = class(TInterfacedObject, IFoo<IInterface>) + private + FFoo : IFoo<IInterface>; + public + property Foo : IFoo<IInterface> read FFoo implements IFoo<IInterface>; + end; + +implementation + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/include file2.inc b/References/DelphiAST/Test/Snippets/include file2.inc new file mode 100644 index 000000000..da96d6b7f --- /dev/null +++ b/References/DelphiAST/Test/Snippets/include file2.inc @@ -0,0 +1 @@ +{$DEFINE TESTINCLUDE2} \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/includefile.inc b/References/DelphiAST/Test/Snippets/includefile.inc new file mode 100644 index 000000000..e4a0c8ffe --- /dev/null +++ b/References/DelphiAST/Test/Snippets/includefile.inc @@ -0,0 +1 @@ +{$DEFINE TESTINCLUDE} \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/includefile.pas b/References/DelphiAST/Test/Snippets/includefile.pas new file mode 100644 index 000000000..8d5ec0cf7 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/includefile.pas @@ -0,0 +1,17 @@ +{$I includefile.inc} +{$INCLUDE 'include file2.inc'} +unit includefile; + +interface + +{$IFNDEF TESTINCLUDE} + this must be ignored +{$ENDIF} + +{$IFNDEF TESTINCLUDE2} + this must be ignored +{$ENDIF} + +implementation + +end. diff --git a/References/DelphiAST/Test/Snippets/isnotnotin.pas b/References/DelphiAST/Test/Snippets/isnotnotin.pas new file mode 100644 index 000000000..d73ed86ba --- /dev/null +++ b/References/DelphiAST/Test/Snippets/isnotnotin.pas @@ -0,0 +1,18 @@ +unit isnotnotin; + +interface + +procedure Test; + +implementation + +procedure Test; +var + a: array of Integer; + b: TObject; +begin + if 1 not in a and b is not TButton then + Exit; +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/managedrecords.pas b/References/DelphiAST/Test/Snippets/managedrecords.pas new file mode 100644 index 000000000..021db1611 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/managedrecords.pas @@ -0,0 +1,25 @@ +unit managedrecords; + +interface + +type + TMyRecord = record + Value: Integer; + class operator Initialize (out Dest: TMyRecord); + class operator Finalize(var Dest: TMyRecord); + end; + +implementation + +class operator TMyRecord.Initialize (out Dest: TMyRecord); +begin + Dest.Value := 10; + Log('created' + IntToHex (Integer(Pointer(@Dest)))); +end; + +class operator TMyRecord.Finalize(var Dest: TMyRecord); +begin + Log('destroyed' + IntToHex (Integer(Pointer(@Dest)))); +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/messagemethod.pas b/References/DelphiAST/Test/Snippets/messagemethod.pas new file mode 100644 index 000000000..497a869f5 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/messagemethod.pas @@ -0,0 +1,13 @@ +unit externalfunction; + +interface + +type + TMyClass = class + strict protected + procedure ProcessMsg(var Msg: TMessage); message WM_USER; + end; + +implementation + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/multiline.pas b/References/DelphiAST/Test/Snippets/multiline.pas new file mode 100644 index 000000000..d28a1db56 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/multiline.pas @@ -0,0 +1,24 @@ +unit multilie; + +interface + +implementation + +const + Str1 = 'Str''Str'; + Str2 = ''; + Str3 = ''' + TEST + STRING + '''; + Str4 = ''''' + TEST ''' + STRING + '''''; + Str5 = ''''' + TEST + '''' + STRING + text'''' + '''''; +end. diff --git a/References/DelphiAST/Test/Snippets/nonalignedrecords.pas b/References/DelphiAST/Test/Snippets/nonalignedrecords.pas new file mode 100644 index 000000000..021db1611 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/nonalignedrecords.pas @@ -0,0 +1,25 @@ +unit managedrecords; + +interface + +type + TMyRecord = record + Value: Integer; + class operator Initialize (out Dest: TMyRecord); + class operator Finalize(var Dest: TMyRecord); + end; + +implementation + +class operator TMyRecord.Initialize (out Dest: TMyRecord); +begin + Dest.Value := 10; + Log('created' + IntToHex (Integer(Pointer(@Dest)))); +end; + +class operator TMyRecord.Finalize(var Dest: TMyRecord); +begin + Log('destroyed' + IntToHex (Integer(Pointer(@Dest)))); +end; + +end. \ No newline at end of file diff --git a/References/DelphiAST/Test/Snippets/noreturn.pas b/References/DelphiAST/Test/Snippets/noreturn.pas new file mode 100644 index 000000000..a0ff43112 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/noreturn.pas @@ -0,0 +1,18 @@ +unit experimentals experimental; + +interface + +type + TExperimentalClass = class + Field: Integer; + end; + +procedure someExperimentalProc(); noreturn; + +implementation + +procedure someExperimentalProc(); noreturn; +begin +end; + +end. diff --git a/References/DelphiAST/Test/Snippets/numbers.pas b/References/DelphiAST/Test/Snippets/numbers.pas new file mode 100644 index 000000000..4d0d849ff --- /dev/null +++ b/References/DelphiAST/Test/Snippets/numbers.pas @@ -0,0 +1,20 @@ +unit constset; + +interface + +implementation + +procedure Test; +var + A,B,C: Integer; + D: Double; +begin + A := 123123_; + B := $_1241_3_; + C := %_01011; + D := 12_3_.12_3_; + A := 1____________23123_; + B := $_12_________41_3_; +end; + +end. diff --git a/References/DelphiAST/Test/Snippets/pointerchars.pas b/References/DelphiAST/Test/Snippets/pointerchars.pas new file mode 100644 index 000000000..cf729081d --- /dev/null +++ b/References/DelphiAST/Test/Snippets/pointerchars.pas @@ -0,0 +1,28 @@ +unit pointerchars; + +interface + +implementation + +procedure FormKeyPress(Sender: TObject; var Key: Char); +var + P: PInteger; + Arr: array of Integer; +begin + if Arr[P^] > 0 then + begin + // some code + end; + + if Key = ^\ then + begin + //some code + end; + + if Key = ^M then + begin + //some code + end; +end; + +end. diff --git a/References/DelphiAST/Test/Snippets/properties.pas b/References/DelphiAST/Test/Snippets/properties.pas new file mode 100644 index 000000000..3539831b8 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/properties.pas @@ -0,0 +1,24 @@ +unit properties; + +interface + +type + TProps = class + public + property Name: string read FName write FName; + property ReadableName: string read FName; + property WriteableName: string write FName; + property Redeclared; + property Width: TWidth read GetWidth write SetWidth stored IsWidthStored default 50; + property Tag: Integer read FTag write FTag default 0; + property Indexed[Index: integer]: string read GetByIndex write SetByIndexed; + property ReadableIndexed[Index: integer]: string read GetByIndex; + property WriteableIndexed[Index: integer]: string write SetByIndexed; + property DefaultIndexed[Index: integer]: string read GetByIndex write SetByIndexed; default; + property DefaultReadableIndexed[Index: integer]: string read GetByIndex; default; + property DefaultWriteableIndexed[Index: integer]: string write SetByIndexed; default; + end; + +implementation + +end. diff --git a/References/DelphiAST/Test/Snippets/strictvisibility.pas b/References/DelphiAST/Test/Snippets/strictvisibility.pas new file mode 100644 index 000000000..0c84f5de6 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/strictvisibility.pas @@ -0,0 +1,13 @@ +unit strictvisibility; + +interface + +type + TStrictClass = class + strict private + strict protected + end; + +implementation + +end. diff --git a/References/DelphiAST/Test/Snippets/ternaryop.pas b/References/DelphiAST/Test/Snippets/ternaryop.pas new file mode 100644 index 000000000..627b1586f --- /dev/null +++ b/References/DelphiAST/Test/Snippets/ternaryop.pas @@ -0,0 +1,17 @@ +unit ternaryop; + +interface + +procedure Test; + +implementation + +procedure Test; +var + a,b: Integer; +begin + a := if True then 1 else 2; + b := if a > 2 then 5 else 6 * 18; +end; + +end. diff --git a/References/DelphiAST/Test/Snippets/tryexcept.pas b/References/DelphiAST/Test/Snippets/tryexcept.pas new file mode 100644 index 000000000..32a0a3610 --- /dev/null +++ b/References/DelphiAST/Test/Snippets/tryexcept.pas @@ -0,0 +1,54 @@ +unit tryexcept; + +interface + +implementation + +procedure DoStuff; +var + O: MyUnit.TMyObject; +begin + try + DoSomething; + except + Log('DoSomething failed'); + end; + + try + DoSomethingElse + except + on E: Exception do + begin + LogError(E); + end; + end; + + try + DoCrazyStuff; + except + + on EFileNotFound do + begin + LogHint('File not found. Does''nt matter.'); + end; + + on MyUnit.EFileNotFound do + begin + LogHint('MyUnit file not found. Does''nt matter.'); + end; + + on E: MyUnit.ECriticalError do + begin + LogError('MyUnit Critical error: ' + E.Message); + end; + + on E: Exception do + begin + LogError(E); + end + else + LogError('Unknown error'); + end; +end; + +end. diff --git a/References/DelphiAST/Test/Snippets/umlauts.pas b/References/DelphiAST/Test/Snippets/umlauts.pas new file mode 100644 index 000000000..7fe399d87 Binary files /dev/null and b/References/DelphiAST/Test/Snippets/umlauts.pas differ diff --git a/References/DelphiAST/Test/Snippets/whitespacearoundifdefcondition.pas b/References/DelphiAST/Test/Snippets/whitespacearoundifdefcondition.pas new file mode 100644 index 000000000..13204dc6f --- /dev/null +++ b/References/DelphiAST/Test/Snippets/whitespacearoundifdefcondition.pas @@ -0,0 +1,23 @@ +unit whitespacearoundifdefcondition; + +interface + +implementation + +{$DEFINE CPUX86} + +procedure Foo; +{$IFDEF CPUX86 } +begin +end; +{$ENDIF} + +procedure Foo2; +{$IF defined(CPUX86) } +begin +end; +{$ENDIF} + +initialization + +end. diff --git a/References/DelphiAST/Test/uMainForm.dfm b/References/DelphiAST/Test/uMainForm.dfm new file mode 100644 index 000000000..6198193bf --- /dev/null +++ b/References/DelphiAST/Test/uMainForm.dfm @@ -0,0 +1,44 @@ +object Form2: TForm2 + Left = -3 + Top = 81 + Caption = 'DelphiAST Test Application' + ClientHeight = 231 + ClientWidth = 687 + Color = clBtnFace + Font.Charset = DEFAULT_CHARSET + Font.Color = clWindowText + Font.Height = -11 + Font.Name = 'Tahoma' + Font.Style = [] + OldCreateOrder = True + DesignSize = ( + 687 + 231) + PixelsPerInch = 96 + TextHeight = 13 + object memLog: TMemo + Left = 0 + Top = 0 + Width = 687 + Height = 193 + Anchors = [akLeft, akTop, akRight, akBottom] + Font.Charset = RUSSIAN_CHARSET + Font.Color = clWindowText + Font.Height = -13 + Font.Name = 'Lucida Console' + Font.Style = [] + ParentFont = False + ScrollBars = ssBoth + TabOrder = 0 + end + object btnRun: TButton + Left = 604 + Top = 198 + Width = 75 + Height = 25 + Anchors = [akRight, akBottom] + Caption = 'Run' + TabOrder = 1 + OnClick = btnRunClick + end +end diff --git a/References/DelphiAST/Test/uMainForm.pas b/References/DelphiAST/Test/uMainForm.pas new file mode 100644 index 000000000..0c9b92776 --- /dev/null +++ b/References/DelphiAST/Test/uMainForm.pas @@ -0,0 +1,102 @@ +unit uMainForm; + +{$IFDEF FPC}{$MODE Delphi}{$ENDIF} + +interface + +uses + {$IFNDEF FPC} + Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, + Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, + {$ELSE} + SysUtils, Variants, Classes, Controls, Forms, StdCtrls, + {$ENDIF} + SimpleParser.Lexer.Types; + +type + TIncludeHandler = class(TInterfacedObject, IIncludeHandler) + private + FPath: string; + public + constructor Create(const Path: string); + function GetIncludeFileContent(const ParentFileName, IncludeName: string; + out Content: string; out FileName: string): Boolean; + end; + + TForm2 = class(TForm) + memLog: TMemo; + btnRun: TButton; + procedure btnRunClick(Sender: TObject); + private + { Private declarations } + public + { Public declarations } + end; + +var + Form2: TForm2; + +implementation + +uses + FileCtrl, IOUtils, DelphiAST, DelphiAST.Classes; + +{$R *.dfm} + +procedure TForm2.btnRunClick(Sender: TObject); +var + Path, FileName: string; + SyntaxTree: TSyntaxNode; +begin + memLog.Clear; + + Path := ExtractFilePath(Application.ExeName) + 'Snippets\'; + if not SelectDirectory('Select Folder', '', Path) then + Exit; + + for FileName in TDirectory.GetFiles(Path, '*.pas', TSearchOption.soAllDirectories) do + begin + try + SyntaxTree := TPasSyntaxTreeBuilder.Run(FileName, False, TIncludeHandler.Create(Path)); + try + memLog.Lines.Add('OK: ' + FileName); + finally + SyntaxTree.Free; + end; + except + on E: Exception do + begin + memLog.Lines.Add('FAILED: ' + FileName); + memLog.Lines.Add(' ' + E.ClassName); + memLog.Lines.Add(' ' + E.Message); + memLog.Repaint; + end; + end; + end; +end; + +{ TIncludeHandler } + +constructor TIncludeHandler.Create(const Path: string); +begin + inherited Create; + FPath := Path; +end; + +function TIncludeHandler.GetIncludeFileContent(const ParentFileName, IncludeName: string; + out Content: string; out FileName: string): Boolean; +var + FileContent: TStringList; +begin + FileContent := TStringList.Create; + try + FileName := TPath.Combine(FPath, IncludeName); + FileContent.LoadFromFile(FileName); + Content := FileContent.Text; + Result := True; + finally + FileContent.Free; + end; +end; + +end. diff --git a/Source/Alcinoe.CodeProfiler.inc b/Source/Alcinoe.CodeProfiler.inc new file mode 100644 index 000000000..3cb3e4c89 --- /dev/null +++ b/Source/Alcinoe.CodeProfiler.inc @@ -0,0 +1,67 @@ +var + ALCodeProfilerEnabled: Boolean = True; + ALCodeProfilerServerName: String = ''; + +// Do not group calls. Each function/procedure call generates one row. +// The result will be displayed as a call tree, for example: +// procedure A - 1 call - 310 ms +// procedure B - 1 call - 215 ms +// procedure C - 1 call - 14 ms +// procedure C - 1 call - 11 ms +// procedure D - 1 call - 85 ms +// procedure C - 1 call - 21 ms +// procedure B - 1 call - 35 ms +// procedure B - 1 call - 41 ms +// procedure C - 1 call - 22 ms +// +// NOTE: This option can use a lot of memory. If you enable it, it is recommended +// to limit profiling to the specific code you want to measure by using +// ALCodeProfilerStart and ALCodeProfilerStop. +{.$DEFINE ALCodeProfilerHistoryGroupNone} + +// Group calls by procedure ID. +// The result will be displayed as a flat grid, for example: +// procedure A - 1 call - 310 ms +// procedure B - 3 calls - 291 ms +// procedure C - 4 calls - 68 ms +// procedure D - 1 call - 85 ms +// +// NOTE: This is the fastest option and has the lowest impact on each function call. +{.$DEFINE ALCodeProfilerHistoryGroupByProcID} + +{$IF defined(ALCodeProfilerHistoryGroupByProcID)} +// Ignore the thread ID. The calls made from every thread are merged together +// instead of producing one row per procedure and per thread, for example: +// procedure A - 3 calls - 310 ms = 1 call from the main thread + 2 calls from a background thread +// +// NOTE: This option is only available with ALCodeProfilerHistoryGroupByProcID. +// All the threads then share the same metrics, which are updated atomically, +// so it slightly increases the cost of each function call, but it also greatly +// reduces the memory usage as only one history is allocated for the whole +// process instead of one per thread. +{.$DEFINE ALCodeProfilerIgnoreThreadID} +{$ENDIF} + +// Group calls by call stack. +// The result will be displayed as a grouped call tree, for example: +// procedure A - 1 call - 310 ms +// procedure B - 3 calls - 291 ms +// procedure C - 3 calls - 46 ms +// procedure D - 1 call - 85 ms +// procedure C - 1 call - 22 ms +{$DEFINE ALCodeProfilerHistoryGroupByCallStack} + +// Capacity of the history, in number of rows. +// +// NOTE: With ALCodeProfilerHistoryGroupByProcID the history is a flat array +// indexed by the procedure ID, so it only needs to be big enough to hold the +// highest procedure ID of ALCodeProfilerProcIDMap.txt. The value below is +// updated by the Alcinoe Code Profiler GUI each time the markers are inserted +// or removed, so there is no reason to edit it by hand. +{$IF defined(ALCodeProfilerHistoryGroupByProcID)} +const + ALCodeProfilerHistoryCapacity = 1000000; +{$ELSE} +const + ALCodeProfilerHistoryCapacity = 1000000; {1 000 000 * 32 Bytes = 32 MB or with gap = 2 097 152 * 32 Bytes = 67.11 MB} +{$ENDIF} diff --git a/Source/Alcinoe.CodeProfiler.pas b/Source/Alcinoe.CodeProfiler.pas index 21ae9cdcb..402390096 100644 --- a/Source/Alcinoe.CodeProfiler.pas +++ b/Source/Alcinoe.CodeProfiler.pas @@ -3,38 +3,70 @@ interface {$I Alcinoe.inc} +{$I Alcinoe.CodeProfiler.inc} + +{$IF defined(ALCodeProfilerIgnoreThreadID) and (not defined(ALCodeProfilerHistoryGroupByProcID))} + {$MESSAGE ERROR 'ALCodeProfilerIgnoreThreadID is only available with ALCodeProfilerHistoryGroupByProcID'} +{$ENDIF} type TALProcMetrics = record public + {$IF defined(ALCodeProfilerHistoryGroupNone)} ExecutionID: Cardinal; ParentExecutionID: Cardinal; ProcID: Cardinal; ThreadID: Cardinal; StartTimeStamp: Int64; ElapsedTicks: Int64; + {$ELSEIF defined(ALCodeProfilerHistoryGroupByProcID)} + ProcID: Cardinal; + // Always 0 when ALCodeProfilerIgnoreThreadID is defined. The field is kept + // in all cases so that the layout of the .dat file never changes. + ThreadID: Cardinal; + CallCount: Cardinal; + ElapsedTicks: Int64; + {$ELSEIF defined(ALCodeProfilerHistoryGroupByCallStack)} + HashCode: Integer; + ProcID: Cardinal; + MetricsID: Cardinal; + ParentMetricsID: Cardinal; + ThreadID: Cardinal; + CallCount: Cardinal; + ElapsedTicks: Int64; + {$ENDIF} end; + PALProcMetrics = ^TALProcMetrics; procedure ALCodeProfilerEnterProc(const aProcID : Cardinal); procedure ALCodeProfilerExitProc(const aProcID : Cardinal); procedure ALCodeProfilerStart; -procedure ALCodeProfilerStop(Const ASaveHistories: Boolean = True); +procedure ALCodeProfilerStop; function ALCodeProfilerIsrunning: Boolean; const - ALCodeProfilerProcMetricsFilename = 'ALCodeProfilerProcMetrics.dat'; ALCodeProfilerProcIDMapFilename = 'ALCodeProfilerProcIDMap.txt'; + {$IF defined(ALCodeProfilerHistoryGroupNone)} + ALCodeProfilerProcMetricsFilename: String = 'ALCodeProfilerProcMetrics.None.dat'; + {$ELSEIF defined(ALCodeProfilerHistoryGroupByProcID)} + ALCodeProfilerProcMetricsFilename: String = 'ALCodeProfilerProcMetrics.ByProcID.dat'; + {$ELSEIF defined(ALCodeProfilerHistoryGroupByCallStack)} + ALCodeProfilerProcMetricsFilename: String = 'ALCodeProfilerProcMetrics.ByCallStack.dat'; + {$ENDIF} ALCodeProfilerRegistryPath = 'Software\MagicFoundation\Alcinoe\CodeProfiler'; ALCodeProfilerDataStoragePathKey = 'DataStoragePath'; ALCodeProfilerMillisecondsPerTick = 0.0001; var ALCodeProfilerAppStartTimeStamp: Int64; - ALCodeProfilerServerName: String; + implementation uses + {$IF defined(ALCodeProfilerHistoryGroupByCallStack)} + System.Hash, + {$ENDIF} {$IF defined(MSWindows)} System.Win.Registry, Winapi.Windows, @@ -59,20 +91,27 @@ implementation System.Classes, System.Generics.Collections, System.Diagnostics, - System.IOUtils, - Alcinoe.FileUtils, - Alcinoe.Common; + System.IOUtils; {**} Type TALStopWatchProcMetrics = record private + {$IF defined(ALCodeProfilerHistoryGroupNone)} class var ExecutionIDSequence: cardinal; + {$ENDIF} public + {$IF defined(ALCodeProfilerHistoryGroupNone)} ExecutionID: Cardinal; ParentExecutionID: Cardinal; + {$ENDIF} ProcID: Cardinal; + {$IF defined(ALCodeProfilerHistoryGroupByCallStack)} + ParentMetricsID: Cardinal; + {$ENDIF} + {$IF not defined(ALCodeProfilerIgnoreThreadID)} ThreadID: Cardinal; + {$ENDIF} StopWatch: TStopWatch; end; @@ -82,10 +121,8 @@ TALProcMetricsStack = class(TObject) FArray: TALStopWatchProcMetricsArray; FCount: NativeInt; FCapacity: NativeInt; - procedure Grow; virtual; + procedure Grow; procedure SetCapacity(NewCapacity: NativeInt); - public - destructor Destroy; override; end; TALProcMetricsArray = array of TALProcMetrics; @@ -94,24 +131,35 @@ TALProcMetricsHistory = class(TObject) FArray: TALProcMetricsArray; FCount: NativeInt; FCapacity: NativeInt; - FIsOrphaned: Boolean; - procedure Grow; virtual; + {$IF defined(ALCodeProfilerHistoryGroupByCallStack)} + FGrowThreshold: NativeInt; + procedure Rehash(NewCapPow2: NativeInt); + function GetBucketIndex(const AProcID, AParentMetricsID: Cardinal; const AHashCode: Integer): NativeInt; + function Hash(const AProcID, AParentMetricsID: Cardinal): Integer; + {$ENDIF} + procedure Grow; procedure SetCapacity(NewCapacity: NativeInt); - public - constructor Create; virtual; - destructor Destroy; override; + procedure Clear; end; {*******} threadvar ALProcMetricsStack: TALProcMetricsStack; + +{*******} +{$IF defined(ALCodeProfilerIgnoreThreadID)} +// All the threads share the same history, so that the metrics of a procedure +// are merged together whatever the thread it was called from. +var ALProcMetricsHistory: TALProcMetricsHistory; - ALIsInCodeProfiler: Boolean; +{$ELSE} +threadvar + ALProcMetricsHistory: TALProcMetricsHistory; +{$ENDIF} {*} var ALProcMetricsHistories: TList<TALProcMetricsHistory>; - ALCodeProfilerEnabled: Boolean; ALProcMetricsLock: TLightweightMREW; ALProcMetricsFilename: String; {$IF defined(IOS) or defined(ANDROID)} @@ -122,6 +170,12 @@ TALProcMetricsHistory = class(TObject) Type TALCodeProfilerLogType = (VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT); +{**************************************************} +{$IF defined(ALCodeProfilerHistoryGroupByCallStack)} +const + EMPTY_HASH = -1; +{$ENDIF} + {**************************} procedure ALCodeProfilerLog( Const Tag: String; @@ -175,12 +229,6 @@ procedure ALCodeProfilerLog( {$ENDIF} end; -{*************************************} -destructor TALProcMetricsStack.Destroy; -begin - SetCapacity(0); -end; - {*********************************} procedure TALProcMetricsStack.Grow; begin @@ -196,38 +244,194 @@ procedure TALProcMetricsStack.SetCapacity(NewCapacity: NativeInt); end; end; -{***************************************} -constructor TALProcMetricsHistory.Create; -begin - inherited; - FIsOrphaned := False; -end; - -{***************************************} -destructor TALProcMetricsHistory.Destroy; -begin - SetCapacity(0); -end; - {***********************************} procedure TALProcMetricsHistory.Grow; begin + {$IF defined(ALCodeProfilerHistoryGroupByCallStack)} + + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Generics.Collections.TDictionary<K,V>.Grow was not updated and adjust the IFDEF'} + {$ENDIF} + + var LNewCap: NativeInt := Length(FArray) * 2; + if LNewCap = 0 then + LNewCap := 4; + Rehash(LNewCap); + + {$ELSE} + SetCapacity(GrowCollection(FCapacity, FCount + 1)); + + {$ENDIF} end; {******************************************************************} procedure TALProcMetricsHistory.SetCapacity(NewCapacity: NativeInt); begin + {$IF defined(ALCodeProfilerHistoryGroupByCallStack)} + + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Generics.Collections.TDictionary<K,V>.SetCapacity was not updated and adjust the IFDEF'} + {$MESSAGE WARN 'Check if System.Generics.Collections.TDictionary<K,V>.InternalSetCapacity was not updated and adjust the IFDEF'} + {$ENDIF} + + // Ensure at least one empty slot for GetBucketIndex to terminate. + Inc(NewCapacity); + if FCapacity <> NewCapacity then begin + if NewCapacity < FCount then + ErrorArgumentOutOfRange; + + if NewCapacity = 0 then Rehash(0) + else begin + var LNewCap: NativeInt := 4; + while LNewCap shr 1 <= NewCapacity do // 50% + LNewCap := LNewCap shl 1; + Rehash(LNewCap); + end + end; + + {$ELSE} + if NewCapacity <> FCapacity then begin SetLength(FArray, NewCapacity); FCapacity := NewCapacity; end; + + {$ENDIF} end; +{************************************} +procedure TALProcMetricsHistory.Clear; +begin + + {$IF defined(ALCodeProfilerHistoryGroupByCallStack)} + + FCount := 0; + SetLength(FArray, 0); + FCapacity := 0; + FGrowThreshold := 0; + + {$ELSE} + + FCount := 0; + + {$ENDIF} + +end; + +{**************************************************} +{$IF defined(ALCodeProfilerHistoryGroupByCallStack)} +procedure TALProcMetricsHistory.Rehash(NewCapPow2: NativeInt); +begin + + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Generics.Collections.TDictionary<K,V>.Rehash was not updated and adjust the IFDEF'} + {$ENDIF} + + if NewCapPow2 = Length(FArray) then + Exit + else if NewCapPow2 < 0 then + OutOfMemoryError; + + var LOldArray: TALProcMetricsArray := FArray; + var LNewArray: TALProcMetricsArray; + + SetLength(LNewArray, NewCapPow2); + var P: PALProcMetrics := PALProcMetrics(LNewArray); + for var i := 0 to Length(LNewArray) - 1 do begin + P^.HashCode := EMPTY_HASH; + Inc(P); + end; + FArray := LNewArray; + FGrowThreshold := NewCapPow2 shr 1; // 50% + + P := PALProcMetrics(LOldArray); + for var i := 0 to Length(LOldArray) - 1 do begin + raise Exception.Create( + 'Rehash is not implemented right now because MetricsID and ParentMetricsID ' + + 'reference positions in the array, which would become invalid after rehashing. ' + + 'The array is currently sized large enough to avoid calling Rehash.'); + if P^.HashCode <> EMPTY_HASH then begin + var j := not GetBucketIndex(P^.ProcID, P^.ParentMetricsID, P^.HashCode); + FArray[j] := P^; + end; + Inc(P); + end; + +end; +{$ENDIF} + +{********************************************************} +{$IF defined(ALCodeProfilerHistoryGroupByCallStack)} +function TALProcMetricsHistory.GetBucketIndex(const AProcID, AParentMetricsID: Cardinal; const AHashCode: Integer): NativeInt; +begin + + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Generics.Collections.TDictionary<K,V>.GetBucketIndex was not updated and adjust the IFDEF'} + {$ENDIF} + + var L: NativeInt := Length(FArray); + if L = 0 then + Exit(not High(NativeInt)); + + Result := AHashCode and (L - 1); + var P: PALProcMetrics := @FArray[Result]; + while True do begin + var LHashCode := P^.HashCode; + + // Not found: return complement of insertion point. + if LHashCode = EMPTY_HASH then + Exit(not Result); + + // Found: return location. + if (LHashCode = AHashCode) and (P^.ProcID = AProcID) and (P^.ParentMetricsID = AParentMetricsID) then + Exit(Result); + + Inc(Result); + Inc(P); + if Result >= L then begin + Result := 0; + P := @FArray[0]; + end; + end; + +end; +{$ENDIF} + +{********************************************************} +{$IF defined(ALCodeProfilerHistoryGroupByCallStack)} +function TALProcMetricsHistory.Hash(const AProcID, AParentMetricsID: Cardinal): Integer; +const + PositiveMask = Integer.MaxValue; +begin + + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Generics.Collections.TDictionary<K,V>.GetBucketIndex was not updated and adjust the IFDEF'} + {$ENDIF} + + {$IFOPT Q+} + {$DEFINE Q_ON} + {$Q-} + {$ENDIF} + var LKey: UInt64 := (UInt64(AProcID) shl 32) or UInt64(AParentMetricsID); + // Double-Abs to avoid -MaxInt and MinInt problems. + // Not using compiler-Abs because we *must* get a positive integer; + // for compiler, Abs(Low(Integer)) is a null op. + Result := PositiveMask and ((PositiveMask and THashFNV1a32.GetHashValue(LKey, SizeOf(LKey))) + 1); + {$IFDEF Q_ON} + {$Q+} + {$UNDEF Q_ON} + {$ENDIF} + +end; +{$ENDIF} + {**********************************************************************************************} procedure ALCodeProfilerSaveHistory(const AProcMetricsHistory: TALProcMetricsHistory); overload; begin + {$IF defined(ALCodeProfilerHistoryGroupNone) or defined(ALCodeProfilerHistoryGroupByCallStack)} if AProcMetricsHistory.FCount = 0 then exit; + {$ENDIF} //-- If ALProcMetricsFilename = '' then begin {$IF defined(MSWindows)} @@ -248,86 +452,99 @@ procedure ALCodeProfilerSaveHistory(const AProcMetricsHistory: TALProcMetricsHis end else {$ENDIF} - ALProcMetricsFilename := TPath.Combine(ALGetTempPathW, ALCodeProfilerProcMetricsFilename); + ALProcMetricsFilename := TPath.Combine(System.IOUtils.TPath.GetTempPath, ALCodeProfilerProcMetricsFilename); if TFile.Exists(ALProcMetricsFilename) then TFile.Delete(ALProcMetricsFilename); end; //-- var LfileStream: TFileStream; + {$IF defined(ALCodeProfilerHistoryGroupByProcID) or defined(ALCodeProfilerHistoryGroupByCallStack)} + if Tfile.Exists(ALProcMetricsFilename) then Tfile.Delete(ALProcMetricsFilename); + LfileStream := TFileStream.Create(ALProcMetricsFilename, fmCreate); + {$ELSE} if Tfile.Exists(ALProcMetricsFilename) then LfileStream := TFileStream.Create(ALProcMetricsFilename, fmOpenWrite) else LfileStream := TFileStream.Create(ALProcMetricsFilename, fmCreate); + {$ENDIF} try LfileStream.Position := LfileStream.Size; + {$IF defined(ALCodeProfilerHistoryGroupNone)} LfileStream.WriteBuffer(AProcMetricsHistory.FArray[0], AProcMetricsHistory.FCount * SizeOf(TALProcMetrics)); + {$ELSEIF defined(ALCodeProfilerHistoryGroupByProcID)} + for var I := Low(AProcMetricsHistory.FArray) to High(AProcMetricsHistory.FArray) do + if AProcMetricsHistory.FArray[I].CallCount <> 0 then + LfileStream.WriteBuffer(AProcMetricsHistory.FArray[I], SizeOf(TALProcMetrics)); + {$ELSEIF defined(ALCodeProfilerHistoryGroupByCallStack)} + for var I := Low(AProcMetricsHistory.FArray) to High(AProcMetricsHistory.FArray) do + if AProcMetricsHistory.FArray[I].HashCode <> EMPTY_HASH then + LfileStream.WriteBuffer(AProcMetricsHistory.FArray[I], SizeOf(TALProcMetrics)); + {$ENDIF} finally LFileStream.Free; end; - //-- - AProcMetricsHistory.FCount := 0; end; -{********************************************************************} -procedure ALCodeProfilerPurgeHistories(const ASaveHistories: boolean); +{*************************************} +procedure ALCodeProfilerPurgeHistories; begin ALProcMetricsLock.BeginWrite; try + for var I := ALProcMetricsHistories.Count - 1 downto 0 do begin - if ASaveHistories then ALCodeProfilerSaveHistory(ALProcMetricsHistories[i]); - if ALProcMetricsHistories[i].FIsOrphaned then ALProcMetricsHistories.ExtractAt(i).Free - else ALProcMetricsHistories[i].FCount := 0; + ALCodeProfilerSaveHistory(ALProcMetricsHistories[i]); + {$IF defined(ALCodeProfilerHistoryGroupNone)} + ALProcMetricsHistories[i].Clear; + {$ENDIF} end; + + if ALCodeProfilerServerName <> '' then begin + var LGuid: TGUID; + if CreateGUID(LGuid) <> S_OK then RaiseLastOSError; + var LGuidStr: String; + SetLength(LGuidStr, 32); + StrLFmt( + PChar(LGuidStr), 32,'%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x', + [LGuid.D1, LGuid.D2, LGuid.D3, LGuid.D4[0], LGuid.D4[1], LGuid.D4[2], LGuid.D4[3], + LGuid.D4[4], LGuid.D4[5], LGuid.D4[6], LGuid.D4[7]]); + var LTmpProcMetricsFilename := ALProcMetricsFilename + '~' + LGuidStr; + TFile.Move(ALProcMetricsFilename, LTmpProcMetricsFilename); + {$IF defined(IOS) or defined(ANDROID)} + TThread.CreateAnonymousThread( + procedure + begin + {$ENDIF} + var LHTTPClient := TNetHTTPClient.Create(nil); + try + Try + var LFileStream := TFileStream.Create(LTmpProcMetricsFilename, fmOpenRead or fmShareDenyWrite); + try + var LHeaders: TNetHeaders; + setlength(LHeaders, 1); + LHeaders[0].Name := 'Content-Type'; + LHeaders[0].Value := 'application/octet-stream'; + LHTTPClient.Post(ALCodeProfilerServerName, LFileStream, nil{AResponseContent}, LHeaders); + finally + LFileStream.Free; + end; + Except + On E: Exception do + ALCodeProfilerLog('ALCodeProfiler', E.Message, TALCodeProfilerLogType.ERROR); + End; + finally + TFile.Delete(LTmpProcMetricsFilename); + LHTTPClient.Free; + end; + {$IF defined(IOS) or defined(ANDROID)} + end).Start; + {$ENDIF} + end; + finally ALProcMetricsLock.EndWrite; end; - //-- - if ALCodeProfilerServerName <> '' then begin - var LGuid: TGUID; - if CreateGUID(LGuid) <> S_OK then RaiseLastOSError; - var LGuidStr: String; - SetLength(LGuidStr, 32); - StrLFmt( - PChar(LGuidStr), 32,'%.8x%.4x%.4x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x', - [LGuid.D1, LGuid.D2, LGuid.D3, LGuid.D4[0], LGuid.D4[1], LGuid.D4[2], LGuid.D4[3], - LGuid.D4[4], LGuid.D4[5], LGuid.D4[6], LGuid.D4[7]]); - var LTmpProcMetricsFilename := ALProcMetricsFilename + '~' + LGuidStr; - TFile.Move(ALProcMetricsFilename, LTmpProcMetricsFilename); - {$IF defined(IOS) or defined(ANDROID)} - TThread.CreateAnonymousThread( - procedure - begin - {$ENDIF} - var LHTTPClient := TNetHTTPClient.Create(nil); - try - Try - var LFileStream := TFileStream.Create(LTmpProcMetricsFilename, fmOpenRead or fmShareDenyWrite); - try - var LHeaders: TNetHeaders; - setlength(LHeaders, 1); - LHeaders[0].Name := 'Content-Type'; - LHeaders[0].Value := 'application/octet-stream'; - LHTTPClient.Post(ALCodeProfilerServerName, LFileStream, nil{AResponseContent}, LHeaders); - finally - LFileStream.Free; - end; - Except - On E: Exception do - ALCodeProfilerLog('ALCodeProfiler', E.Message, TALCodeProfilerLogType.ERROR); - End; - finally - TFile.Delete(LTmpProcMetricsFilename); - LHTTPClient.Free; - end; - {$IF defined(IOS) or defined(ANDROID)} - end).Start; - {$ENDIF} - end; end; {**********************************************************} procedure ALCodeProfilerEnterProc(const aProcID : Cardinal); begin - if ALIsInCodeProfiler then exit; - ALIsInCodeProfiler := True; - //-- if ALCodeProfilerEnabled then begin var LProcMetricsStack := ALProcMetricsStack; if LProcMetricsStack = nil then begin @@ -339,26 +556,79 @@ procedure ALCodeProfilerEnterProc(const aProcID : Cardinal); if LProcMetricsStack.FCount = LProcMetricsStack.FCapacity then LProcMetricsStack.Grow; inc(LProcMetricsStack.FCount); With LProcMetricsStack.FArray[LProcMetricsStack.FCount - 1] do begin + {$IF defined(ALCodeProfilerHistoryGroupNone)} ExecutionID := AtomicIncrement(TALStopWatchProcMetrics.ExecutionIDSequence); + {$ENDIF} if LProcMetricsStack.FCount > 1 then begin + {$IF defined(ALCodeProfilerHistoryGroupByCallStack)} + var LProcMetricsHistory := ALProcMetricsHistory; + if LProcMetricsHistory = nil then begin + ALProcMetricsHistory := TALProcMetricsHistory.Create; + ALProcMetricsHistory.SetCapacity(ALCodeProfilerHistoryCapacity); {with the default capacity: 1 000 000 * 32 Bytes = 32 MB or with gap = 2 097 152 * 32 Bytes = 67.11 MB} + LProcMetricsHistory := ALProcMetricsHistory; + ALProcMetricsLock.BeginWrite; + try + ALProcMetricsHistories.Add(LProcMetricsHistory); + finally + ALProcMetricsLock.EndWrite; + end; + end; + ALProcMetricsLock.BeginRead; + try + var LParentProcID: Cardinal := LProcMetricsStack.FArray[LProcMetricsStack.FCount - 2].ProcID; + var LParentParentMetricsID := LProcMetricsStack.FArray[LProcMetricsStack.FCount - 2].ParentMetricsID; + var LHashCode: Integer := LProcMetricsHistory.Hash(LParentProcID, LParentParentMetricsID); + var LParentMetricsID: NativeInt := LProcMetricsHistory.GetBucketIndex(LParentProcID, LParentParentMetricsID, LHashCode); + if LParentMetricsID < 0 then begin + if LProcMetricsHistory.FCount >= LProcMetricsHistory.FGrowThreshold then begin + LProcMetricsHistory.Grow; + LParentMetricsID := LProcMetricsHistory.GetBucketIndex(LParentProcID, LParentParentMetricsID, LHashCode); + end; + inc(LProcMetricsHistory.FCount); + LParentMetricsID := not LParentMetricsID; + With LProcMetricsHistory.FArray[LParentMetricsID] do begin + HashCode := LHashCode; + ProcID := LParentProcID; + MetricsID := LParentMetricsID; + ParentMetricsID := LParentParentMetricsID; + ThreadID := LProcMetricsStack.FArray[LProcMetricsStack.FCount - 2].ThreadID; + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Diagnostics.TStopwatch.InitStopwatchType was not updated and adjust the IFDEF'} + {$ENDIF} + CallCount := 0; + ElapsedTicks := 0; + end; + end; + ParentMetricsID := LParentMetricsID; + finally + ALProcMetricsLock.EndRead; + end; + {$ELSEIF defined(ALCodeProfilerHistoryGroupNone)} ParentExecutionID := LProcMetricsStack.FArray[LProcMetricsStack.FCount - 2].ExecutionID; + {$ENDIF} + {$IF not defined(ALCodeProfilerIgnoreThreadID)} ThreadID := LProcMetricsStack.FArray[LProcMetricsStack.FCount - 2].ThreadID; + {$ENDIF} end else begin + {$IF defined(ALCodeProfilerHistoryGroupByCallStack)} + ParentMetricsID := 0; + {$ELSEIF defined(ALCodeProfilerHistoryGroupNone)} ParentExecutionID := 0; + {$ENDIF} + {$IF not defined(ALCodeProfilerIgnoreThreadID)} var LCurrentThreadID := TThread.CurrentThread.ThreadID; if LCurrentThreadID = MainThreadID then ThreadID := 0 else begin ThreadID := LCurrentThreadID mod 4294967295; if ThreadID = 0 then ThreadID := 1; end; + {$ENDIF} end; ProcID := AProcID; StopWatch := TStopWatch.StartNew; end; end; - //-- - ALIsInCodeProfiler := False; end; {*********************************************************} @@ -376,32 +646,21 @@ TStopwatchAccessPrivate = record end; begin - if ALIsInCodeProfiler then exit; - ALIsInCodeProfiler := True; - //-- var LProcMetricsStack := ALProcMetricsStack; if LProcMetricsStack <> nil then begin if not ALCodeProfilerEnabled then begin ALProcMetricsStack.Free; ALProcMetricsStack := nil; - if ALProcMetricsHistory <> nil then begin - ALProcMetricsLock.BeginRead; - try - ALProcMetricsHistory.FIsOrphaned := true; - ALProcMetricsHistory := nil; - finally - ALProcMetricsLock.EndRead; - end; - end; end else if LProcMetricsStack.FCount <> 0 then begin var LProcMetricsStackLastIndex: integer := LProcMetricsStack.FCount - 1; LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.Stop; //-- var LProcMetricsHistory := ALProcMetricsHistory; + {$IF not defined(ALCodeProfilerIgnoreThreadID)} if LProcMetricsHistory = nil then begin ALProcMetricsHistory := TALProcMetricsHistory.Create; - ALProcMetricsHistory.SetCapacity(1000000); {1 000 000 * 32 Bytes = 32 MB} + ALProcMetricsHistory.SetCapacity(ALCodeProfilerHistoryCapacity); {with the default capacity: 1 000 000 * 32 Bytes = 32 MB or with gap = 2 097 152 * 32 Bytes = 67.11 MB} LProcMetricsHistory := ALProcMetricsHistory; ALProcMetricsLock.BeginWrite; try @@ -410,10 +669,23 @@ TStopwatchAccessPrivate = record ALProcMetricsLock.EndWrite; end; end; + {$ENDIF} //-- ALProcMetricsLock.BeginRead; try - if LProcMetricsHistory.FCount = LProcMetricsHistory.FCapacity then LProcMetricsHistory.Grow; + {$IF defined(ALCodeProfilerHistoryGroupNone)} + if (LProcMetricsHistory.FCount = LProcMetricsHistory.FCapacity) then begin + if (LProcMetricsHistory.FCount >= 100_000_000) {100_000_000 * 32 Bytes = 3.2 GB} then begin + ALProcMetricsLock.EndRead; + try + ALCodeProfilerPurgeHistories; + finally + ALProcMetricsLock.BeginRead; + end; + end; + if LProcMetricsHistory.FCount = LProcMetricsHistory.FCapacity then + LProcMetricsHistory.Grow; + end; inc(LProcMetricsHistory.FCount); With LProcMetricsHistory.FArray[LProcMetricsHistory.FCount - 1] do begin ExecutionID := LProcMetricsStack.FArray[LProcMetricsStackLastIndex].ExecutionID; @@ -436,6 +708,107 @@ TStopwatchAccessPrivate = record Raise Exception.create('Error 55533349-EC72-404D-B113-CA32C518012F') {$ENDIF} end; + {$ELSEIF defined(ALCodeProfilerHistoryGroupByProcID)} + var LProcID: Cardinal := LProcMetricsStack.FArray[LProcMetricsStackLastIndex].ProcID; + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Generics.Collections.TDictionary<K,V>.TryAdd was not updated and adjust the IFDEF'} + {$ENDIF} + {$IF defined(ALCodeProfilerIgnoreThreadID)} + // All the threads update the very same record, so the metrics must be + // updated atomically. + With LProcMetricsHistory.FArray[LProcID] do begin + ProcID := LProcID; + ThreadID := 0; + AtomicIncrement(CallCount); + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Diagnostics.TStopwatch.InitStopwatchType was not updated and adjust the IFDEF'} + {$ENDIF} + {$IF defined(MSWINDOWS)} + var LTickFrequency: Double; + if not TStopwatch.IsHighResolution then LTickFrequency := 1.0 + else LTickFrequency := 10000000.0 / LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.Frequency; + AtomicIncrement(ElapsedTicks, Trunc(LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.ElapsedTicks * LTickFrequency)); + {$ELSEIF defined(POSIX)} + AtomicIncrement(ElapsedTicks, LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.ElapsedTicks); + {$ELSE} + Raise Exception.create('Error 5FE96C7E-ABFA-4AE2-84E5-39EFF2E83BBB') + {$ENDIF} + end; + {$ELSE} + With LProcMetricsHistory.FArray[LProcID] do begin + ProcID := LProcID; + ThreadID := LProcMetricsStack.FArray[LProcMetricsStackLastIndex].ThreadID; + Inc(CallCount); + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Diagnostics.TStopwatch.InitStopwatchType was not updated and adjust the IFDEF'} + {$ENDIF} + {$IF defined(MSWINDOWS)} + var LTickFrequency: Double; + if not TStopwatch.IsHighResolution then LTickFrequency := 1.0 + else LTickFrequency := 10000000.0 / LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.Frequency; + ElapsedTicks := ElapsedTicks + Trunc(LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.ElapsedTicks * LTickFrequency); + {$ELSEIF defined(POSIX)} + ElapsedTicks := ElapsedTicks + LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.ElapsedTicks; + {$ELSE} + Raise Exception.create('Error 8CB28339-29A0-4276-80AA-F8CD5E447CE5') + {$ENDIF} + end; + {$ENDIF} + {$ELSEIF defined(ALCodeProfilerHistoryGroupByCallStack)} + var LProcID: Cardinal := LProcMetricsStack.FArray[LProcMetricsStackLastIndex].ProcID; + var LParentMetricsID: Cardinal := LProcMetricsStack.FArray[LProcMetricsStackLastIndex].ParentMetricsID; + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Generics.Collections.TDictionary<K,V>.TryAdd was not updated and adjust the IFDEF'} + {$ENDIF} + var LHashCode: Integer := LProcMetricsHistory.Hash(LProcID, LParentMetricsID); + var LIndex: NativeInt := LProcMetricsHistory.GetBucketIndex(LProcID, LParentMetricsID, LHashCode); + if LIndex >= 0 then begin + With LProcMetricsHistory.FArray[LIndex] do begin + Inc(CallCount); + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Diagnostics.TStopwatch.InitStopwatchType was not updated and adjust the IFDEF'} + {$ENDIF} + {$IF defined(MSWINDOWS)} + var LTickFrequency: Double; + if not TStopwatch.IsHighResolution then LTickFrequency := 1.0 + else LTickFrequency := 10000000.0 / LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.Frequency; + ElapsedTicks := ElapsedTicks + Trunc(LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.ElapsedTicks * LTickFrequency); + {$ELSEIF defined(POSIX)} + ElapsedTicks := ElapsedTicks + LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.ElapsedTicks; + {$ELSE} + Raise Exception.create('Error 8CB28339-29A0-4276-80AA-F8CD5E447CE5') + {$ENDIF} + end; + end + else begin + if LProcMetricsHistory.FCount >= LProcMetricsHistory.FGrowThreshold then begin + LProcMetricsHistory.Grow; + LIndex := LProcMetricsHistory.GetBucketIndex(LProcID, LParentMetricsID, LHashCode); + end; + inc(LProcMetricsHistory.FCount); + With LProcMetricsHistory.FArray[not LIndex] do begin + HashCode := LHashCode; + ProcID := LProcID; + MetricsID := not LIndex; + ParentMetricsID := LParentMetricsID; + ThreadID := LProcMetricsStack.FArray[LProcMetricsStackLastIndex].ThreadID; + CallCount := 1; + {$IFNDEF ALCompilerVersionSupported131} + {$MESSAGE WARN 'Check if System.Diagnostics.TStopwatch.InitStopwatchType was not updated and adjust the IFDEF'} + {$ENDIF} + {$IF defined(MSWINDOWS)} + var LTickFrequency: Double; + if not TStopwatch.IsHighResolution then LTickFrequency := 1.0 + else LTickFrequency := 10000000.0 / LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.Frequency; + ElapsedTicks := Trunc(LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.ElapsedTicks * LTickFrequency); + {$ELSEIF defined(POSIX)} + ElapsedTicks := LProcMetricsStack.FArray[LProcMetricsStackLastIndex].StopWatch.ElapsedTicks; + {$ELSE} + Raise Exception.create('Error 8CB28339-29A0-4276-80AA-F8CD5E447CE5') + {$ENDIF} + end; + end; + {$ENDIF} dec(LProcMetricsStack.FCount); finally ALProcMetricsLock.EndRead; @@ -444,20 +817,9 @@ TStopwatchAccessPrivate = record (TThread.CurrentThread.ThreadID <> MainThreadID) then begin ALProcMetricsStack.Free; ALProcMetricsStack := nil; - If ALProcMetricsHistory <> nil then begin - ALProcMetricsLock.BeginRead; - try - ALProcMetricsHistory.FIsOrphaned := true; - ALProcMetricsHistory := nil; - finally - ALProcMetricsLock.EndRead; - end; - end; end; end; end; - //-- - ALIsInCodeProfiler := False; end; {****************************} @@ -466,11 +828,10 @@ procedure ALCodeProfilerStart; ALCodeProfilerEnabled := True; end; -{*****************************************************************} -procedure ALCodeProfilerStop(Const ASaveHistories: Boolean = True); +{***************************} +procedure ALCodeProfilerStop; Begin ALCodeProfilerEnabled := False; - ALCodeProfilerPurgeHistories(ASaveHistories); End; {****************************************} @@ -486,7 +847,7 @@ procedure ALCodeProfilerApplicationEventHandler(const Sender: TObject; const M: if (M is TApplicationEventMessage) and ((M as TApplicationEventMessage).value.Event = TApplicationEvent.BecameActive) then begin if ALCodeProfilerAppActivatedBefore then - ALCodeProfilerPurgeHistories(ALCodeProfilerEnabled{ASaveHistories}) + ALCodeProfilerPurgeHistories; else ALCodeProfilerAppActivatedBefore := True; end; @@ -494,19 +855,19 @@ procedure ALCodeProfilerApplicationEventHandler(const Sender: TObject; const M: {$ENDIF} initialization - {$IF defined(DEBUG)} - ALLog('Alcinoe.CodeProfiler','initialization'); - {$ENDIF} - ALIsInCodeProfiler := False; ALCodeProfilerAppStartTimeStamp := TStopWatch.GetTimeStamp; + {$IF defined(ALCodeProfilerHistoryGroupNone)} TALStopWatchProcMetrics.ExecutionIDSequence := 0; + {$ENDIF} //ALProcMetricsLock := ?? There is no TLightweightMREW.Create; initialization is done through the TLightweightMREW.Initialize class operator instead - ALCodeProfilerEnabled := True; ALProcMetricsFilename := ''; - ALCodeProfilerServerName := ''; //-- ALProcMetricsHistory := TALProcMetricsHistory.Create; + {$IF defined(ALCodeProfilerHistoryGroupNone)} ALProcMetricsHistory.SetCapacity(25000000); {25 000 000 * 32 Bytes = 800MB} + {$ELSE} + ALProcMetricsHistory.SetCapacity(ALCodeProfilerHistoryCapacity); {with the default capacity: 1 000 000 = 2 097 152 (with gap) * 32 Bytes = 67.11 MB} + {$ENDIF} //-- ALProcMetricsHistories := TList<TALProcMetricsHistory>.Create; ALProcMetricsHistories.Add(ALProcMetricsHistory); @@ -517,12 +878,9 @@ initialization {$ENDIF} finalization - {$IF defined(DEBUG)} - ALLog('Alcinoe.CodeProfiler','finalization'); - {$ENDIF} {$IF (not defined(IOS)) and (not defined(ANDROID))} // At this point, all background threads must have completed. - ALCodeProfilerPurgeHistories(ALCodeProfilerEnabled{ASaveHistories}); + ALCodeProfilerPurgeHistories; {$ENDIF} ALCodeProfilerEnabled := False; //-- diff --git a/Source/Alcinoe.inc b/Source/Alcinoe.inc index 9045b4f26..52fce46f7 100644 --- a/Source/Alcinoe.inc +++ b/Source/Alcinoe.inc @@ -68,4 +68,6 @@ {$ZEROBASEDSTRINGS OFF} +{$TYPEDADDRESS OFF} + {.$Define ALUIAutomationEnabled} diff --git a/Tools/CodeProfiler/_Source/CodeProfiler.dpr b/Tools/CodeProfiler/_Source/CodeProfiler.dpr index 8b033bf0c..51fa04346 100644 --- a/Tools/CodeProfiler/_Source/CodeProfiler.dpr +++ b/Tools/CodeProfiler/_Source/CodeProfiler.dpr @@ -8,7 +8,7 @@ uses {$R *.res} begin - ALCodeProfilerStop(False{ASaveHistory}); + ALCodeProfilerStop; Application.Initialize; Application.CreateForm(TMainForm, MainForm); Application.Run; diff --git a/Tools/CodeProfiler/_Source/CodeProfiler.dproj b/Tools/CodeProfiler/_Source/CodeProfiler.dproj index 30fc5d119..d30b2a2d1 100644 --- a/Tools/CodeProfiler/_Source/CodeProfiler.dproj +++ b/Tools/CodeProfiler/_Source/CodeProfiler.dproj @@ -185,4 +185,4 @@ </ProjectExtensions> <Import Condition="Exists('$(BDS)\Bin\CodeGear.Delphi.Targets')" Project="$(BDS)\Bin\CodeGear.Delphi.Targets"/> <Import Condition="Exists('$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj')" Project="$(APPDATA)\Embarcadero\$(BDSAPPDATABASEDIR)\$(PRODUCTVERSION)\UserTools.proj"/> -</Project> \ No newline at end of file +</Project> diff --git a/Tools/CodeProfiler/_Source/Main.dfm b/Tools/CodeProfiler/_Source/Main.dfm index 0a1d08be6..374553807 100644 --- a/Tools/CodeProfiler/_Source/Main.dfm +++ b/Tools/CodeProfiler/_Source/Main.dfm @@ -2,8 +2,8 @@ object MainForm: TMainForm Left = 377 Top = 296 Caption = 'Alcinoe CodeProfiler' - ClientHeight = 900 - ClientWidth = 1080 + ClientHeight = 985 + ClientWidth = 1264 Color = clBtnFace Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText @@ -17,27 +17,25 @@ object MainForm: TMainForm object MainPageControl: TcxPageControl Left = 0 Top = 0 - Width = 1080 - Height = 868 + Width = 1264 + Height = 953 Align = alClient TabOrder = 0 Properties.ActivePage = InstrumentationTabSheet Properties.CustomButtons.Buttons = <> - ExplicitHeight = 768 - ClientRectBottom = 863 + ClientRectBottom = 948 ClientRectLeft = 5 - ClientRectRight = 1075 + ClientRectRight = 1259 ClientRectTop = 37 object InstrumentationTabSheet: TcxTabSheet Caption = 'Source Code Instrumentation' ImageIndex = 0 OnResize = InstrumentationTabSheetResize - ExplicitHeight = 726 object InstructionPanel: TdxPanel Left = 0 Top = 0 - Width = 1070 - Height = 424 + Width = 1254 + Height = 353 Align = alTop Color = 16448250 TabOrder = 0 @@ -60,7 +58,7 @@ object MainForm: TMainForm Properties.WordWrap = True TabOrder = 0 Transparent = True - Width = 1062 + Width = 1246 end object cxLabel2: TcxLabel AlignWithMargins = True @@ -78,7 +76,7 @@ object MainForm: TMainForm Properties.WordWrap = True TabOrder = 1 Transparent = True - Width = 1062 + Width = 1246 end object cxLabel3: TcxLabel AlignWithMargins = True @@ -86,7 +84,9 @@ object MainForm: TMainForm Top = 107 Margins.Left = 16 Align = alTop - Caption = '2. Add Alcinoe profiler markers to your code.' + Caption = + '2. Click the "Insert Markers" button below to add profiler marke' + + 'rs to your code.' ParentFont = False Style.Font.Charset = DEFAULT_CHARSET Style.Font.Color = clWindowText @@ -97,8 +97,7 @@ object MainForm: TMainForm Properties.WordWrap = True TabOrder = 2 Transparent = True - ExplicitTop = 74 - Width = 1049 + Width = 1233 end object cxLabel4: TcxLabel AlignWithMargins = True @@ -117,8 +116,7 @@ object MainForm: TMainForm Properties.WordWrap = True TabOrder = 3 Transparent = True - ExplicitTop = 107 - Width = 1049 + Width = 1233 end object cxLabel5: TcxLabel AlignWithMargins = True @@ -128,8 +126,9 @@ object MainForm: TMainForm Align = alTop Caption = '4. If you are using Android or iOS, send the app to the backgrou' + - 'nd and then bring it back to the foreground to generate the perf' + - 'ormance file.' + 'nd and bring it back to the foreground to generate the performan' + + 'ce file. On Windows and macOS, the performance file will be gene' + + 'rated when you close the app.' ParentFont = False Style.Font.Charset = DEFAULT_CHARSET Style.Font.Color = clWindowText @@ -140,57 +139,18 @@ object MainForm: TMainForm Properties.WordWrap = True TabOrder = 4 Transparent = True - Width = 1049 + Width = 1233 end - object cxLabel6: TcxLabel + object LastInstructionLabel: TcxLabel AlignWithMargins = True Left = 16 - Top = 285 + Top = 308 Margins.Left = 16 - Align = alTop - Caption = '6. Perform the performance analysis.' - ParentFont = False - Style.Font.Charset = DEFAULT_CHARSET - Style.Font.Color = clWindowText - Style.Font.Height = -17 - Style.Font.Name = 'Segoe UI' - Style.Font.Style = [] - Style.IsFontAssigned = True - Properties.WordWrap = True - TabOrder = 5 - Transparent = True - Width = 1049 - end - object cxLabel8: TcxLabel - AlignWithMargins = True - Left = 3 - Top = 318 - Align = alTop - Caption = 'Note:' - ParentFont = False - Style.Font.Charset = DEFAULT_CHARSET - Style.Font.Color = clWindowText - Style.Font.Height = -17 - Style.Font.Name = 'Segoe UI' - Style.Font.Style = [fsBold] - Style.IsFontAssigned = True - Properties.WordWrap = True - TabOrder = 6 - Transparent = True - ExplicitTop = 206 - Width = 1062 - end - object LastInstructionLabel: TcxLabel - AlignWithMargins = True - Left = 3 - Top = 351 - Margins.Bottom = 8 + Margins.Bottom = 16 Align = alTop Caption = - 'On Windows, the performance file is stored in the CodeProfiler d' + - 'ata folder if the app is running locally; otherwise, it is saved' + - ' in the user'#39's document folder. On macOS, iOS, and Android, it i' + - 's always stored in the user'#39's document folder.' + '6. Go to the Performance Analysis tab, click the Load Data butto' + + 'n, and run the analysis.' ParentFont = False Style.Font.Charset = DEFAULT_CHARSET Style.Font.Color = clWindowText @@ -199,10 +159,10 @@ object MainForm: TMainForm Style.Font.Style = [] Style.IsFontAssigned = True Properties.WordWrap = True - TabOrder = 7 + TabOrder = 5 Transparent = True - ExplicitTop = 239 - Width = 1062 + ExplicitTop = 285 + Width = 1233 end object cxLabel13: TcxLabel AlignWithMargins = True @@ -210,7 +170,9 @@ object MainForm: TMainForm Top = 74 Margins.Left = 16 Align = alTop - Caption = '1. Specify the server IP and port for the listening process.' + Caption = + '1. If you run the program on a remote device (such as Android or' + + ' iOS), specify the server IP and port for the listening process.' ParentFont = False Style.Font.Charset = DEFAULT_CHARSET Style.Font.Color = clWindowText @@ -219,9 +181,9 @@ object MainForm: TMainForm Style.Font.Style = [] Style.IsFontAssigned = True Properties.WordWrap = True - TabOrder = 8 + TabOrder = 6 Transparent = True - Width = 1049 + Width = 1233 end object cxLabel14: TcxLabel AlignWithMargins = True @@ -231,10 +193,12 @@ object MainForm: TMainForm Align = alTop Caption = '5. If you specified the server IP and port in step 1, the perfor' + - 'mance file will be received automatically. After receiving it, s' + - 'imply reload the data; otherwise, download the data from the use' + - 'r'#39's document folder and place it in the CodeProfiler data folder' + - '.' + 'mance file will be received automatically. Otherwise, if you are' + + ' running the program on a remote device, download the data from ' + + 'the user'#39's documents folder and place it in the CodeProfiler dat' + + 'a folder. Note: On Windows, the performance file is directly sto' + + 'red in the CodeProfiler data folder if the app is running locall' + + 'y; otherwise, it is saved in the user'#39's document folder.' ParentFont = False Style.Font.Charset = DEFAULT_CHARSET Style.Font.Color = clWindowText @@ -243,44 +207,45 @@ object MainForm: TMainForm Style.Font.Style = [] Style.IsFontAssigned = True Properties.WordWrap = True - TabOrder = 9 + TabOrder = 7 Transparent = True - Width = 1049 + Width = 1233 end end object dxPanel2: TdxPanel AlignWithMargins = True Left = 0 - Top = 432 - Width = 1070 - Height = 386 + Top = 361 + Width = 1254 + Height = 542 Margins.Left = 0 Margins.Top = 8 Margins.Right = 0 Margins.Bottom = 8 Align = alClient TabOrder = 1 - ExplicitTop = 393 - ExplicitHeight = 325 + ExplicitTop = 432 + ExplicitHeight = 471 object SourcesPathMemo: TcxMemo AlignWithMargins = True Left = 8 - Top = 164 + Top = 362 Margins.Left = 8 Margins.Right = 8 - Margins.Bottom = 12 + Margins.Bottom = 0 Align = alClient TabOrder = 0 - ExplicitHeight = 147 - Height = 208 - Width = 1052 + ExplicitTop = 299 + ExplicitHeight = 118 + Height = 126 + Width = 1236 end object cxLabel9: TcxLabel AlignWithMargins = True Left = 8 - Top = 134 + Top = 332 Margins.Left = 8 - Margins.Top = 8 + Margins.Top = 0 Margins.Right = 8 Margins.Bottom = 0 Align = alTop @@ -289,12 +254,13 @@ object MainForm: TMainForm ' or filename per line. Prefix a name with '#39'!'#39' to ignore the file' Properties.WordWrap = True TabOrder = 1 - Width = 1052 + ExplicitTop = 269 + Width = 1236 end object dxPanel3: TdxPanel Left = 0 - Top = 95 - Width = 1068 + Top = 148 + Width = 1252 Height = 31 Align = alTop Frame.Borders = [] @@ -338,6 +304,7 @@ object MainForm: TMainForm Margins.Right = 8 Margins.Bottom = 0 Align = alLeft + Properties.OnChange = HttpServerNameEditPropertiesChange TabOrder = 2 Width = 358 end @@ -356,32 +323,114 @@ object MainForm: TMainForm Width = 71 end end + object cxLabel15: TcxLabel + AlignWithMargins = True + Left = 8 + Top = 8 + Margins.Left = 8 + Margins.Top = 8 + Margins.Right = 8 + Margins.Bottom = 8 + Align = alTop + Caption = + 'Path to Alcinoe.CodeProfiler.inc, the include file where the opt' + + 'ions below are stored' + Properties.WordWrap = True + TabOrder = 6 + Width = 1236 + end + object dxPanel5: TdxPanel + Left = 0 + Top = 43 + Width = 1252 + Height = 31 + Align = alTop + Frame.Borders = [] + LookAndFeel.NativeStyle = False + LookAndFeel.SkinName = 'Foggy' + TabOrder = 7 + object BrowseCodeProfilerIncFilenameBtn: TcxButton + AlignWithMargins = True + Left = 1204 + Top = 0 + Width = 40 + Height = 31 + Margins.Left = 8 + Margins.Top = 0 + Margins.Right = 8 + Margins.Bottom = 0 + Align = alRight + Caption = '...' + TabOrder = 0 + OnClick = BrowseCodeProfilerIncFilenameBtnClick + end + object CodeProfilerIncFilenameEdit: TcxTextEdit + AlignWithMargins = True + Left = 8 + Top = 0 + Margins.Left = 8 + Margins.Top = 0 + Margins.Right = 8 + Margins.Bottom = 0 + Align = alClient + Properties.OnChange = CodeProfilerIncFilenameEditPropertiesChange + TabOrder = 1 + Width = 1180 + end + end + object dxPanel6: TdxPanel + Left = 0 + Top = 74 + Width = 1252 + Height = 31 + Align = alTop + Frame.Borders = [] + LookAndFeel.NativeStyle = False + LookAndFeel.SkinName = 'Foggy' + TabOrder = 8 + ExplicitLeft = 16 + ExplicitTop = 59 + object CodeProfilerEnabledCheckBox: TcxCheckBox + AlignWithMargins = True + Left = 8 + Top = 3 + Margins.Left = 8 + Align = alLeft + Caption = 'Start profiling as soon as the application starts' + Properties.OnChange = CodeProfilerEnabledCheckBoxPropertiesChange + TabOrder = 0 + ExplicitLeft = -1 + ExplicitTop = 19 + end + end object cxLabel10: TcxLabel AlignWithMargins = True Left = 8 - Top = 60 + Top = 113 Margins.Left = 8 Margins.Top = 8 Margins.Right = 8 Margins.Bottom = 8 Align = alTop Caption = - 'Specify the IP address and port to automatically receive the per' + - 'formance file, then update the markers in your code.' + '(Optional) Specify the IP address and port of this computer to a' + + 'utomatically receive the performance file. Not required for loca' + + 'l execution.' Properties.WordWrap = True TabOrder = 3 - Width = 1052 + Width = 1236 end object dxPanel1: TdxPanel Left = 0 - Top = 0 - Width = 1068 + Top = 488 + Width = 1252 Height = 52 - Align = alTop + Align = alBottom Frame.Borders = [] LookAndFeel.NativeStyle = False LookAndFeel.SkinName = 'Foggy' TabOrder = 4 + ExplicitTop = 417 object InsertProfilerMarkersBtn: TcxButton Left = 8 Top = 12 @@ -401,16 +450,134 @@ object MainForm: TMainForm OnClick = RemoveProfilerMarkersBtnClick end end + object dxPanel4: TdxPanel + AlignWithMargins = True + Left = 3 + Top = 217 + Width = 1246 + Height = 39 + Margins.Bottom = 0 + Align = alTop + Frame.Borders = [] + LookAndFeel.NativeStyle = False + LookAndFeel.SkinName = 'Foggy' + TabOrder = 5 + ExplicitTop = 182 + object DoNotGroupRadioButton: TcxRadioButton + AlignWithMargins = True + Left = 8 + Top = 3 + Margins.Left = 8 + Align = alLeft + Caption = 'Do not group (Huge memory usage!)' + TabOrder = 0 + OnClick = HistoryGroupModeRadioButtonClick + AutoSize = True + end + object GroupCallsByProcIDRadioButton: TcxRadioButton + AlignWithMargins = True + Left = 347 + Top = 3 + Margins.Left = 32 + Align = alLeft + Caption = 'Group by procedure ID' + TabOrder = 1 + OnClick = HistoryGroupModeRadioButtonClick + AutoSize = True + end + object GroupCallsByCallStackRadioButton: TcxRadioButton + AlignWithMargins = True + Left = 579 + Top = 3 + Margins.Left = 32 + Align = alLeft + Caption = 'Group by call stack (recommended)' + Checked = True + TabOrder = 2 + TabStop = True + OnClick = HistoryGroupModeRadioButtonClick + AutoSize = True + end + end + object dxPanel7: TdxPanel + AlignWithMargins = True + Left = 3 + Top = 256 + Width = 1246 + Height = 39 + Margins.Top = 0 + Align = alTop + Frame.Borders = [] + LookAndFeel.NativeStyle = False + LookAndFeel.SkinName = 'Foggy' + TabOrder = 9 + ExplicitTop = 227 + object IgnoreThreadIDCheckBox: TcxCheckBox + AlignWithMargins = True + Left = 8 + Top = 3 + Margins.Left = 8 + Align = alLeft + Caption = + 'Ignore thread ID (This option is only available with Group by pr' + + 'ocedure ID)' + Properties.OnChange = IgnoreThreadIDCheckBoxPropertiesChange + Style.TransparentBorder = False + TabOrder = 0 + end + end + object cxLabel7: TcxLabel + AlignWithMargins = True + Left = 8 + Top = 302 + Margins.Left = 8 + Margins.Top = 4 + Margins.Right = 8 + Align = alTop + Caption = 'Source Code Paths' + ParentFont = False + Style.Font.Charset = DEFAULT_CHARSET + Style.Font.Color = clWindowText + Style.Font.Height = -17 + Style.Font.Name = 'Segoe UI' + Style.Font.Style = [fsBold] + Style.IsFontAssigned = True + Properties.WordWrap = True + TabOrder = 10 + ExplicitTop = 349 + Width = 1236 + end + object cxLabel16: TcxLabel + AlignWithMargins = True + Left = 8 + Top = 187 + Margins.Left = 8 + Margins.Top = 8 + Margins.Right = 8 + Margins.Bottom = 0 + Align = alTop + Caption = 'Call Grouping (Help is available in Alcinoe.CodeProfiler.inc)' + ParentFont = False + Style.Font.Charset = DEFAULT_CHARSET + Style.Font.Color = clWindowText + Style.Font.Height = -17 + Style.Font.Name = 'Segoe UI' + Style.Font.Style = [fsBold] + Style.IsFontAssigned = True + Properties.WordWrap = True + TabOrder = 11 + ExplicitTop = 195 + Width = 1236 + end end end object PerformanceAnalysisTabSheet: TcxTabSheet Caption = 'Performance Analysis' ImageIndex = 1 - ExplicitHeight = 726 object Panelfilter: TPanel Left = 0 Top = 0 - Width = 1070 + Width = 1254 Height = 89 Margins.Left = 8 Align = alTop @@ -420,30 +587,30 @@ object MainForm: TMainForm TabOrder = 0 OnResize = PanelfilterResize object Label1: TLabel - Left = 152 + Left = 289 Top = 11 Width = 168 Height = 23 Caption = 'Start Timestamp (Min)' end object Label2: TLabel - Left = 520 + Left = 657 Top = 11 Width = 171 Height = 23 Caption = 'Start Timestamp (Max)' end object ProcNameFilterEdit: TcxTextEdit - Left = 152 + Left = 288 Top = 45 TabOrder = 0 TextHint = 'Search for procedure names. Accepts multiple entries separated b' + 'y '#39';'#39 - Width = 913 + Width = 724 end object ApplyFilterBtn: TcxButton - Left = 8 + Left = 143 Top = 45 Width = 124 Height = 31 @@ -462,7 +629,7 @@ object MainForm: TMainForm OnClick = LoadDataBtnClick end object StartTimeStampMinEdit: TcxMaskEdit - Left = 326 + Left = 463 Top = 8 Properties.MaskKind = emkRegExpr Properties.EditMask = '([0-5][0-9]):([0-5][0-9]):([0-9]{3})\.([0-9]{1,4})' @@ -471,7 +638,7 @@ object MainForm: TMainForm Width = 177 end object StartTimeStampMaxEdit: TcxMaskEdit - Left = 698 + Left = 835 Top = 8 Properties.MaskKind = emkRegExpr Properties.EditMask = '([0-5][0-9]):([0-5][0-9]):([0-9]{3})\.([0-9]{1,4})' @@ -479,11 +646,31 @@ object MainForm: TMainForm TextHint = 'mm:ss:zzz.zzzz' Width = 177 end + object ClearDataBtn: TcxButton + Left = 8 + Top = 45 + Width = 124 + Height = 31 + Margins.Right = 8 + Caption = 'Clear Data' + TabOrder = 5 + OnClick = ClearDataBtnClick + end + object ExportToCsvBtn: TcxButton + Left = 143 + Top = 8 + Width = 124 + Height = 31 + Margins.Right = 8 + Caption = 'Export to CSV' + TabOrder = 6 + OnClick = ExportToCsvBtnClick + end end object TreeListProcMetrics: TcxTreeList Left = 0 Top = 89 - Width = 1070 + Width = 1254 Height = 145 Align = alTop Bands = < @@ -494,7 +681,6 @@ object MainForm: TMainForm Font.Height = -17 Font.Name = 'Segoe UI Light' Font.Style = [] - Navigator.Buttons.CustomButtons = <> OptionsBehavior.CopyCaptionsToClipboard = False OptionsData.Editing = False OptionsView.ColumnAutoWidth = True @@ -503,6 +689,19 @@ object MainForm: TMainForm Styles.Background = cxStyleTreeListProcMetricsBackground TabOrder = 1 OnDblClick = TreeListProcMetricsDblClick + object TreeListProcMetricsColumnExecutionID: TcxTreeListColumn + Caption.Text = '_ExecutionID' + DataBinding.ValueType = 'Integer' + Options.Moving = False + Width = 120 + Position.ColIndex = 0 + Position.RowIndex = 0 + Position.BandIndex = 0 + SortOrder = soDescending + SortIndex = 0 + Summary.FooterSummaryItems = <> + Summary.GroupFooterSummaryItems = <> + end object TreeListProcMetricsColumnProcName: TcxTreeListColumn Caption.Text = 'Name' Options.Filtering = False @@ -528,42 +727,42 @@ object MainForm: TMainForm Summary.FooterSummaryItems = <> Summary.GroupFooterSummaryItems = <> end - object TreeListProcMetricsColumnTimeTaken: TcxTreeListColumn - Caption.Text = 'TimeTaken' - DataBinding.ValueType = 'Float' + object TreeListProcMetricsColumnStartTimeStamp: TcxTreeListColumn + Caption.Text = 'Start Timestamp (mm:ss:zzz)' Options.Filtering = False Options.Moving = False Options.Sorting = False - Width = 150 - Position.ColIndex = 4 + Width = 250 + Position.ColIndex = 3 Position.RowIndex = 0 Position.BandIndex = 0 Summary.FooterSummaryItems = <> Summary.GroupFooterSummaryItems = <> + OnGetDisplayText = TreeListProcMetricsColumnStartTimeStampGetDisplayText end - object TreeListProcMetricsColumnStartTimeStamp: TcxTreeListColumn - Caption.Text = 'Start Timestamp (mm:ss:zzz)' + object TreeListProcMetricsColumnCallCount: TcxTreeListColumn + Caption.Text = 'Call Count' + DataBinding.ValueType = 'LargeInt' Options.Filtering = False Options.Moving = False Options.Sorting = False - Width = 250 - Position.ColIndex = 3 + Width = 120 + Position.ColIndex = 4 Position.RowIndex = 0 Position.BandIndex = 0 Summary.FooterSummaryItems = <> Summary.GroupFooterSummaryItems = <> - OnGetDisplayText = TreeListProcMetricsColumnStartTimeStampGetDisplayText end - object TreeListProcMetricsColumnExecutionID: TcxTreeListColumn - Caption.Text = '_ExecutionID' - DataBinding.ValueType = 'Integer' + object TreeListProcMetricsColumnTimeTaken: TcxTreeListColumn + Caption.Text = 'TimeTaken' + DataBinding.ValueType = 'Float' + Options.Filtering = False Options.Moving = False - Width = 120 - Position.ColIndex = 0 + Options.Sorting = False + Width = 150 + Position.ColIndex = 5 Position.RowIndex = 0 Position.BandIndex = 0 - SortOrder = soDescending - SortIndex = 0 Summary.FooterSummaryItems = <> Summary.GroupFooterSummaryItems = <> end @@ -571,8 +770,8 @@ object MainForm: TMainForm object GridProcMetrics: TcxGrid Left = 0 Top = 241 - Width = 1070 - Height = 585 + Width = 1254 + Height = 670 Align = alClient Font.Charset = DEFAULT_CHARSET Font.Color = clWindowText @@ -581,10 +780,7 @@ object MainForm: TMainForm Font.Style = [] ParentFont = False TabOrder = 2 - ExplicitHeight = 485 object GridTableViewProcMetrics: TcxGridTableView - Navigator.Buttons.CustomButtons = <> - ScrollbarAnnotations.CustomAnnotations = <> OnCellDblClick = GridTableViewProcMetricsCellDblClick DataController.Summary.DefaultGroupSummaryItems = < item @@ -603,7 +799,6 @@ object MainForm: TMainForm Kind = skCount Column = GridTableViewProcMetricsColumnProcName end> - DataController.Summary.SummaryGroups = <> DateTimeHandling.Grouping = dtgByDate OptionsBehavior.CellHints = True OptionsBehavior.CopyCaptionsToClipboard = False @@ -644,6 +839,11 @@ object MainForm: TMainForm SortOrder = soAscending Width = 250 end + object GridTableViewProcMetricsColumnCallCount: TcxGridColumn + Caption = 'Call Count' + DataBinding.ValueType = 'LargeInt' + Width = 120 + end object GridTableViewProcMetricsColumnTimeTaken: TcxGridColumn Caption = 'Time Taken' DataBinding.ValueType = 'Float' @@ -659,7 +859,7 @@ object MainForm: TMainForm object cxSplitter1: TcxSplitter Left = 0 Top = 234 - Width = 1070 + Width = 1254 Height = 7 AlignSplitter = salTop end @@ -667,8 +867,8 @@ object MainForm: TMainForm end object MainStatusBar: TdxStatusBar Left = 0 - Top = 868 - Width = 1080 + Top = 953 + Width = 1264 Height = 32 Panels = < item @@ -678,17 +878,16 @@ object MainForm: TMainForm item PanelStyleClassName = 'TdxStatusBarTextPanelStyle' end> - ExplicitTop = 768 end object dxSkinController: TdxSkinController NativeStyle = False SkinName = 'Foggy' - Left = 768 - Top = 120 + Left = 808 + Top = 152 end object cxStyleRepository: TcxStyleRepository - Left = 888 - Top = 120 + Left = 920 + Top = 152 PixelsPerInch = 96 object cxStyleTreeListProcMetricsBackground: TcxStyle AssignedValues = [svColor] @@ -701,7 +900,7 @@ object MainForm: TMainForm OnException = IdHTTPServerException OnListenException = IdHTTPServerListenException OnCommandGet = IdHTTPServerCommandGet - Left = 661 - Top = 118 + Left = 693 + Top = 150 end end diff --git a/Tools/CodeProfiler/_Source/Main.pas b/Tools/CodeProfiler/_Source/Main.pas index e01ffd582..38a15932b 100644 --- a/Tools/CodeProfiler/_Source/Main.pas +++ b/Tools/CodeProfiler/_Source/Main.pas @@ -2,6 +2,9 @@ interface +{$I Alcinoe.inc} +{$SCOPEDENUMS OFF} + uses Vcl.Forms, dxBarBuiltInMenu, cxGraphics, dxUIAClasses, cxControls, cxLookAndFeels, cxLookAndFeelPainters, cxContainer, cxEdit, Vcl.Menus, @@ -31,7 +34,7 @@ interface dxSkinXmas2008Blue, cxTL, cxTLdxBarBuiltInMenu, cxInplaceContainer, cxTreeView, cxTLData, cxSplitter, IdBaseComponent, IdComponent, IdCustomTCPServer, IdCustomHTTPServer, IdHTTPServer, IdContext, dxStatusBar, System.SysUtils, - System.SyncObjs; + System.SyncObjs, cxRadioGroup, cxCheckBox; type @@ -55,6 +58,7 @@ TMainForm = class(TForm) GridTableViewProcMetricsColumnProcName: TcxGridColumn; GridTableViewProcMetricsColumnThreadID: TcxGridColumn; GridTableViewProcMetricsColumnTimeTaken: TcxGridColumn; + GridTableViewProcMetricsColumnCallCount: TcxGridColumn; GridLevelProcMetrics: TcxGridLevel; LoadDataBtn: TcxButton; InstructionPanel: TdxPanel; @@ -63,8 +67,7 @@ TMainForm = class(TForm) cxLabel3: TcxLabel; cxLabel4: TcxLabel; cxLabel5: TcxLabel; - cxLabel6: TcxLabel; - cxLabel8: TcxLabel; + LastInstructionLabel: TcxLabel; dxPanel2: TdxPanel; SourcesPathMemo: TcxMemo; cxLabel9: TcxLabel; @@ -72,8 +75,8 @@ TMainForm = class(TForm) cxSplitter1: TcxSplitter; cxStyleTreeListProcMetricsBackground: TcxStyle; TreeListProcMetricsColumnStartTimeStamp: TcxTreeListColumn; + TreeListProcMetricsColumnCallCount: TcxTreeListColumn; GridTableViewProcMetricsColumnStartTimestamp: TcxGridColumn; - LastInstructionLabel: TcxLabel; StartTimeStampMinEdit: TcxMaskEdit; Label1: TLabel; StartTimeStampMaxEdit: TcxMaskEdit; @@ -90,10 +93,27 @@ TMainForm = class(TForm) MainStatusBar: TdxStatusBar; cxLabel13: TcxLabel; cxLabel14: TcxLabel; + ClearDataBtn: TcxButton; + ExportToCsvBtn: TcxButton; + dxPanel4: TdxPanel; + DoNotGroupRadioButton: TcxRadioButton; + GroupCallsByProcIDRadioButton: TcxRadioButton; + GroupCallsByCallStackRadioButton: TcxRadioButton; + cxLabel15: TcxLabel; + dxPanel5: TdxPanel; + BrowseCodeProfilerIncFilenameBtn: TcxButton; + CodeProfilerIncFilenameEdit: TcxTextEdit; + dxPanel6: TdxPanel; + CodeProfilerEnabledCheckBox: TcxCheckBox; + dxPanel7: TdxPanel; + IgnoreThreadIDCheckBox: TcxCheckBox; + cxLabel7: TcxLabel; + cxLabel16: TcxLabel; procedure InsertProfilerMarkersBtnClick(Sender: TObject); procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure LoadDataBtnClick(Sender: TObject); + procedure ClearDataBtnClick(Sender: TObject); procedure GridTableViewProcMetricsCellDblClick( Sender: TcxCustomGridTableView; ACellViewInfo: TcxGridTableDataCellViewInfo; @@ -108,12 +128,23 @@ TMainForm = class(TForm) procedure TreeListProcMetricsColumnStartTimeStampGetDisplayText(Sender: TcxTreeListColumn; ANode: TcxTreeListNode; var Value: string); procedure PanelfilterResize(Sender: TObject); procedure HttpServerPortEditPropertiesChange(Sender: TObject); + procedure HttpServerNameEditPropertiesChange(Sender: TObject); procedure IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: TIdHTTPRequestInfo; AResponseInfo: TIdHTTPResponseInfo); procedure IdHTTPServerException(AContext: TIdContext; AException: Exception); procedure IdHTTPServerListenException(AThread: TIdListenerThread; AException: Exception); procedure IdHTTPServerConnect(AContext: TIdContext); + procedure ExportToCsvBtnClick(Sender: TObject); + procedure HistoryGroupModeRadioButtonClick(Sender: TObject); + procedure CodeProfilerIncFilenameEditPropertiesChange(Sender: TObject); + procedure BrowseCodeProfilerIncFilenameBtnClick(Sender: TObject); + procedure CodeProfilerEnabledCheckBoxPropertiesChange(Sender: TObject); + procedure IgnoreThreadIDCheckBoxPropertiesChange(Sender: TObject); private const ConfigFilename = 'Config.ini'; + // Default value of ALCodeProfilerHistoryCapacity, used for every history + // group mode but ALCodeProfilerHistoryGroupByProcID, where the capacity + // is deduced from ALCodeProfilerProcIDMap.txt instead. + const DefaultHistoryCapacity = 1000000; private Type TGoBackStackItem = record @@ -122,24 +153,105 @@ TGoBackStackItem = record SortColumnIndex: Integer; SortOrder: TcxGridSortOrder; end; + // Mirrors the ALCodeProfilerHistoryGroupXXX modes selected via the + // radio buttons at runtime, so this tool never needs to be recompiled + // just because Alcinoe.CodeProfiler.inc's active mode changed. + TALCodeProfilerHistoryGroupMode = (hgmNone, hgmByProcID, hgmByCallStack); + // Raw, on-disk layouts. These intentionally mirror the field lists of + // Alcinoe.CodeProfiler.TALProcMetrics for each mode so the compiler + // computes the exact same size/offsets that the profiled app used + // when writing the .dat file. + TALProcMetricsNone = record + ExecutionID: Cardinal; + ParentExecutionID: Cardinal; + ProcID: Cardinal; + ThreadID: Cardinal; + StartTimeStamp: Int64; + ElapsedTicks: Int64; + end; + TALProcMetricsByProcID = record + ProcID: Cardinal; + ThreadID: Cardinal; + CallCount: Cardinal; + ElapsedTicks: Int64; + end; + TALProcMetricsByCallStack = record + HashCode: Integer; + ProcID: Cardinal; + MetricsID: Cardinal; + ParentMetricsID: Cardinal; + ThreadID: Cardinal; + CallCount: Cardinal; + ElapsedTicks: Int64; + end; + // In-memory, mode-agnostic representation decoded from whichever raw + // layout above matches FHistoryGroupMode. Fields not meaningful for + // the current mode are left at 0. + TALProcMetrics = record + ExecutionID: Cardinal; + ParentExecutionID: Cardinal; + ProcID: Cardinal; + MetricsID: Cardinal; + ParentMetricsID: Cardinal; + ThreadID: Cardinal; + StartTimeStamp: Int64; + ElapsedTicks: Int64; + CallCount: Cardinal; + end; + // CSV columns available across the three modes (CSV export). + TALProcMetricsColumnKind = ( + colkExecutionID, colkParentExecutionID, colkProcID, colkMetricsID, colkParentMetricsID, + colkProcName, colkThreadID, colkStartTimeStamp, colkCallCount, colkTimeTaken); private FDataDir: String; + // Set while the UI is being populated from Config.ini or from + // Alcinoe.CodeProfiler.inc, so that the change events raised by that + // population do not write the settings back to Alcinoe.CodeProfiler.inc. + FLoadingSettings: Boolean; FProcIDSequence: Integer; + FHistoryGroupMode: TALCodeProfilerHistoryGroupMode; FProcMetrics: TArray<TALProcMetrics>; FProcIDMap: TALHashedStringListA; FTreeListProcMetricsTailNode: TcxTreeListNode; + FTreeListProcMetricsRootNode: TcxTreeListNode; FFilterProcIDs: THashSet<Cardinal>; FFilterExecutionIDs: THashSet<Cardinal>; FFilterParentExecutionIDs: THashSet<Cardinal>; FFilterStartTimeStampMin: Int64; FFilterStartTimeStampMax: Int64; FOverrideFilterParentExecutionID: Cardinal; + FOverrideFilterThreadID: Cardinal; // High(Cardinal) means "no thread filter" FGoBackStack: TDictionary<Int64{ExecutionID}, TGoBackStackItem>; FHttpServerCriticalSection: TCriticalSection; procedure ResetFilters; + procedure ResetTreeListProcMetrics; Procedure RemoveMarkers(const AFileName: String); Procedure InsertMarkers(const AFileName: String; Const AProcIDMap: TALStringListA); procedure Refresh; + function GetSelectedHistoryGroupModeEnum: TALCodeProfilerHistoryGroupMode; + function GetSelectedHistoryGroupMode: AnsiString; + procedure SelectHistoryGroupMode(const AHistoryGroupMode: String); + procedure UpdateHistoryGroupModeUI; + function GetSelectedProcMetricsFilename: String; + function GetProcMetricsRawRecordSize(const AHistoryGroupMode: TALCodeProfilerHistoryGroupMode): Integer; + procedure DecodeProcMetricsRaw( + const AHistoryGroupMode: TALCodeProfilerHistoryGroupMode; + const ARawBuffer: TBytes; + const AOffset: Integer; + out ARec: TALProcMetrics); + function GetProcMetricsColumnsForMode(const AHistoryGroupMode: TALCodeProfilerHistoryGroupMode): TArray<TALProcMetricsColumnKind>; + function GetProcMetricsColumnName(const AKind: TALProcMetricsColumnKind): AnsiString; + function GetProcMetricsColumnValue( + const AKind: TALProcMetricsColumnKind; + const ARec: TALProcMetrics; + const AProcNames: TDictionary<Cardinal, AnsiString>): AnsiString; + function GetSelectedServerName: AnsiString; + procedure SelectServerName(const AServerName: String); + function GetCodeProfilerIncFilename: String; + function GetHistoryCapacity: Integer; + procedure SaveCodeProfilerIncFile; + procedure LoadCodeProfilerIncFile; + procedure SaveConfigFile; public end; @@ -155,8 +267,13 @@ implementation System.IOUtils, System.Win.Registry, system.IniFiles, + System.Generics.Defaults, winapi.Windows, + DelphiAST, + DelphiAST.Classes, + DelphiAST.Consts, VCL.Dialogs, + VCL.CheckLst, Alcinoe.StringUtils, Alcinoe.FileUtils, Alcinoe.Common; @@ -212,6 +329,459 @@ procedure TMainForm.ResetFilters; FFilterStartTimeStampMin := 0; FFilterStartTimeStampMax := 0; FOverrideFilterParentExecutionID := 0; + FOverrideFilterThreadID := High(Cardinal); +end; + +{*******************************************************************} +procedure TMainForm.ResetTreeListProcMetrics; +begin + TreeListProcMetrics.Clear; + // Always keep a "..." root node visible so the user has an obvious, + // clickable way back to the root instead of having to guess that + // double-clicking empty space resets the drill-down. + FTreeListProcMetricsTailNode := TreeListProcMetrics.Add; + FTreeListProcMetricsTailNode.Texts[TreeListProcMetricsColumnProcName.ItemIndex] := '...'; + FTreeListProcMetricsTailNode.Texts[TreeListProcMetricsColumnExecutionID.ItemIndex] := '0'; + // Leave the ThreadID cell blank for the root node (there is no single + // thread to show); FTreeListProcMetricsRootNode is used instead of this + // column's value to detect the root node and clear the thread filter. + FTreeListProcMetricsRootNode := FTreeListProcMetricsTailNode; +end; + +{*******************************************************************} +function TMainForm.GetSelectedHistoryGroupModeEnum: TALCodeProfilerHistoryGroupMode; +begin + if DoNotGroupRadioButton.Checked then Result := hgmNone + else if GroupCallsByProcIDRadioButton.Checked then Result := hgmByProcID + else Result := hgmByCallStack; +end; + +{*******************************************************************} +function TMainForm.GetSelectedHistoryGroupMode: AnsiString; +begin + case GetSelectedHistoryGroupModeEnum of + hgmNone: Result := 'ALCodeProfilerHistoryGroupNone'; + hgmByProcID: Result := 'ALCodeProfilerHistoryGroupByProcID'; + else Result := 'ALCodeProfilerHistoryGroupByCallStack'; + end; +end; + +{*******************************************************************************} +procedure TMainForm.SelectHistoryGroupMode(const AHistoryGroupMode: String); +begin + if ALSameTextW(AHistoryGroupMode, 'ALCodeProfilerHistoryGroupNone') then DoNotGroupRadioButton.Checked := True + else if ALSameTextW(AHistoryGroupMode, 'ALCodeProfilerHistoryGroupByProcID') then GroupCallsByProcIDRadioButton.Checked := True + else GroupCallsByCallStackRadioButton.Checked := True; +end; + +{*******************************************************************} +procedure TMainForm.UpdateHistoryGroupModeUI; +begin + case GetSelectedHistoryGroupModeEnum of + hgmNone: begin + TreeListProcMetricsColumnStartTimeStamp.Visible := True; + GridTableViewProcMetricsColumnStartTimestamp.Visible := True; + TreeListProcMetricsColumnCallCount.Visible := False; + GridTableViewProcMetricsColumnCallCount.Visible := False; + // The start-timestamp filter only makes sense for individual calls, + // which only exist in hgmNone; grouped modes have no StartTimeStamp. + StartTimeStampMinEdit.Enabled := True; + StartTimeStampMaxEdit.Enabled := True; + end; + else begin + TreeListProcMetricsColumnStartTimeStamp.Visible := False; + GridTableViewProcMetricsColumnStartTimestamp.Visible := False; + TreeListProcMetricsColumnCallCount.Visible := True; + GridTableViewProcMetricsColumnCallCount.Visible := True; + StartTimeStampMinEdit.Enabled := False; + StartTimeStampMaxEdit.Enabled := False; + end; + end; + // ALCodeProfilerIgnoreThreadID is only supported by + // ALCodeProfilerHistoryGroupByProcID. + IgnoreThreadIDCheckBox.Enabled := GetSelectedHistoryGroupModeEnum = hgmByProcID; +end; + +{*******************************************************************} +function TMainForm.GetSelectedProcMetricsFilename: String; +begin + case GetSelectedHistoryGroupModeEnum of + hgmNone: Result := 'ALCodeProfilerProcMetrics.None.dat'; + hgmByProcID: Result := 'ALCodeProfilerProcMetrics.ByProcID.dat'; + else Result := 'ALCodeProfilerProcMetrics.ByCallStack.dat'; + end; +end; + +{*******************************************************************} +function TMainForm.GetProcMetricsRawRecordSize(const AHistoryGroupMode: TALCodeProfilerHistoryGroupMode): Integer; +begin + case AHistoryGroupMode of + hgmNone: Result := SizeOf(TALProcMetricsNone); + hgmByProcID: Result := SizeOf(TALProcMetricsByProcID); + else Result := SizeOf(TALProcMetricsByCallStack); + end; +end; + +{*******************************************************************} +procedure TMainForm.DecodeProcMetricsRaw( + const AHistoryGroupMode: TALCodeProfilerHistoryGroupMode; + const ARawBuffer: TBytes; + const AOffset: Integer; + out ARec: TALProcMetrics); +begin + FillChar(ARec, SizeOf(ARec), 0); + case AHistoryGroupMode of + hgmNone: begin + var LRaw: TALProcMetricsNone; + Move(ARawBuffer[AOffset], LRaw, SizeOf(LRaw)); + ARec.ExecutionID := LRaw.ExecutionID; + ARec.ParentExecutionID := LRaw.ParentExecutionID; + ARec.ProcID := LRaw.ProcID; + ARec.ThreadID := LRaw.ThreadID; + ARec.StartTimeStamp := LRaw.StartTimeStamp; + ARec.ElapsedTicks := LRaw.ElapsedTicks; + end; + hgmByProcID: begin + var LRaw: TALProcMetricsByProcID; + Move(ARawBuffer[AOffset], LRaw, SizeOf(LRaw)); + ARec.ProcID := LRaw.ProcID; + ARec.ThreadID := LRaw.ThreadID; + ARec.CallCount := LRaw.CallCount; + ARec.ElapsedTicks := LRaw.ElapsedTicks; + end; + hgmByCallStack: begin + var LRaw: TALProcMetricsByCallStack; + Move(ARawBuffer[AOffset], LRaw, SizeOf(LRaw)); + ARec.ProcID := LRaw.ProcID; + ARec.MetricsID := LRaw.MetricsID; + ARec.ParentMetricsID := LRaw.ParentMetricsID; + ARec.ThreadID := LRaw.ThreadID; + ARec.CallCount := LRaw.CallCount; + ARec.ElapsedTicks := LRaw.ElapsedTicks; + end; + end; +end; + +{*******************************************************************} +function TMainForm.GetProcMetricsColumnsForMode(const AHistoryGroupMode: TALCodeProfilerHistoryGroupMode): TArray<TALProcMetricsColumnKind>; +begin + case AHistoryGroupMode of + hgmNone: Result := [colkExecutionID, colkParentExecutionID, colkProcID, colkProcName, colkThreadID, colkStartTimeStamp, colkTimeTaken]; + hgmByProcID: Result := [colkProcID, colkProcName, colkThreadID, colkCallCount, colkTimeTaken]; + else Result := [colkProcID, colkMetricsID, colkParentMetricsID, colkProcName, colkThreadID, colkCallCount, colkTimeTaken]; + end; +end; + +{*******************************************************************} +function TMainForm.GetProcMetricsColumnName(const AKind: TALProcMetricsColumnKind): AnsiString; +begin + case AKind of + colkExecutionID: Result := 'ExecutionID'; + colkParentExecutionID: Result := 'ParentExecutionID'; + colkProcID: Result := 'ProcID'; + colkMetricsID: Result := 'MetricsID'; + colkParentMetricsID: Result := 'ParentMetricsID'; + colkProcName: Result := 'ProcName'; + colkThreadID: Result := 'ThreadID'; + colkStartTimeStamp: Result := 'StartTimeStamp'; + colkCallCount: Result := 'CallCount'; + else Result := 'TimeTaken'; + end; +end; + +{*******************************************************************} +function TMainForm.GetProcMetricsColumnValue( + const AKind: TALProcMetricsColumnKind; + const ARec: TALProcMetrics; + const AProcNames: TDictionary<Cardinal, AnsiString>): AnsiString; + + {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~} + function _TicksToMillisecondsStr(const ATicks: Int64): AnsiString; + begin + // 1 tick equals 0.0001 millisecond (ALCodeProfilerMillisecondsPerTick), + // so use integer arithmetic to avoid any rounding/locale issue + Result := ALIntToStrA(ATicks div 10000) + '.' + ALFormatA('%.4d', [ATicks mod 10000]); + end; + +begin + case AKind of + colkExecutionID: Result := ALIntToStrA(ARec.ExecutionID); + colkParentExecutionID: Result := ALIntToStrA(ARec.ParentExecutionID); + colkProcID: Result := ALIntToStrA(ARec.ProcID); + colkMetricsID: Result := ALIntToStrA(ARec.MetricsID); + colkParentMetricsID: Result := ALIntToStrA(ARec.ParentMetricsID); + colkProcName: begin + var LProcName: AnsiString; + if not AProcNames.TryGetValue(ARec.ProcID, LProcName) then LProcName := ''; + Result := LProcName; + end; + colkThreadID: Result := ALIntToStrA(ARec.ThreadID); + colkStartTimeStamp: Result := _TicksToMillisecondsStr(ARec.StartTimeStamp); + colkCallCount: Result := ALIntToStrA(ARec.CallCount); + else Result := _TicksToMillisecondsStr(ARec.ElapsedTicks); + end; +end; + +{*******************************************************************} +function TMainForm.GetSelectedServerName: AnsiString; +begin + if (ALTrim(HttpServerNameEdit.Text) <> '') and (ALTrim(HttpServerPortEdit.Text) <> '') then + Result := AnsiString('http://'+ALTrim(HttpServerNameEdit.Text)+':'+ALTrim(HttpServerPortEdit.Text)) + else + Result := ''; +end; + +{*******************************************************************} +procedure TMainForm.SelectServerName(const AServerName: String); +begin + var LServerName := AServerName; + if ALPosW('http://', LServerName) = 1 then Delete(LServerName, 1, Length('http://')); + var LColonPos := LastDelimiter(':', LServerName); + if LColonPos > 0 then begin + HttpServerNameEdit.Text := ALCopyStr(LServerName, 1, LColonPos - 1); + HttpServerPortEdit.Text := ALCopyStr(LServerName, LColonPos + 1, MaxInt); + end + else begin + HttpServerNameEdit.Text := LServerName; + HttpServerPortEdit.Text := '8080'; + end; +end; + +{*****************************************************************************} +procedure TMainForm.HistoryGroupModeRadioButtonClick(Sender: TObject); +begin + UpdateHistoryGroupModeUI; + SaveCodeProfilerIncFile; +end; + +{*************************************************************************} +procedure TMainForm.IgnoreThreadIDCheckBoxPropertiesChange(Sender: TObject); +begin + SaveCodeProfilerIncFile; +end; + +{**********************************************************} +function TMainForm.GetCodeProfilerIncFilename: String; +begin + Result := ALTrim(CodeProfilerIncFilenameEdit.Text); + if Result = '' then exit; + // A relative path is relative to the folder of this tool, so that the + // default value (..\..\Source\Alcinoe.CodeProfiler.inc) keeps working + // whatever the location of the Alcinoe repository. + if TPath.IsRelativePath(Result) then Result := ALGetModulePathW + Result; + Result := ExpandFileName(Result); +end; + +{****************************************} +function TMainForm.GetHistoryCapacity: Integer; +begin + // With ALCodeProfilerHistoryGroupByProcID the history is a flat array + // indexed by the procedure ID, so it only needs to be big enough to hold the + // highest procedure ID of ALCodeProfilerProcIDMap.txt. When that file does + // not exist (no markers inserted yet, or markers just removed) fall back to + // the default capacity. + Result := DefaultHistoryCapacity; + var LProcIDMapFilename := TPath.Combine(FDataDir, ALCodeProfilerProcIDMapFilename); + if not TFile.Exists(LProcIDMapFilename) then exit; + var LProcIDMap := TALStringListA.Create; + try + LProcIDMap.LoadFromFile(LProcIDMapFilename); + if LProcIDMap.Count = 0 then exit; + var LMaxProcID := 0; + for var I := 0 to LProcIDMap.Count - 1 do begin + var LProcID := ALStrToInt(LProcIDMap.Names[I]); + if LProcID > LMaxProcID then LMaxProcID := LProcID; + end; + // The procedure IDs start at 1, so the array must have LMaxProcID + 1 + // items for FArray[LMaxProcID] to be valid. + Result := LMaxProcID + 1; + finally + ALFreeAndNil(LProcIDMap); + end; +end; + +{*******************************************} +procedure TMainForm.SaveCodeProfilerIncFile; +var + LHistoryGroupMode: AnsiString; + + {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~} + function _DefineLine(const AHistoryGroupModeName: AnsiString): AnsiString; + begin + if ALSameTextA(AHistoryGroupModeName, LHistoryGroupMode) then Result := '{$DEFINE ' + AHistoryGroupModeName + '}' + else Result := '{.$DEFINE ' + AHistoryGroupModeName + '}'; + end; + +begin + // Never write Alcinoe.CodeProfiler.inc while the UI is still being populated + // from Config.ini / Alcinoe.CodeProfiler.inc, else the values just loaded + // would be written back, possibly to a file that is not the selected one yet. + if FLoadingSettings then exit; + var LIncFilename := GetCodeProfilerIncFilename; + if LIncFilename = '' then exit; + LHistoryGroupMode := GetSelectedHistoryGroupMode; + var LContent: AnsiString := + 'var'#10 + + ' ALCodeProfilerEnabled: Boolean = '+ALIfThenA(CodeProfilerEnabledCheckBox.Checked, 'True', 'False')+';'#10 + + ' ALCodeProfilerServerName: String = '''+GetSelectedServerName+''';'#10 + + #10 + + '// Do not group calls. Each function/procedure call generates one row.'#10 + + '// The result will be displayed as a call tree, for example:'#10 + + '// procedure A - 1 call - 310 ms'#10 + + '// procedure B - 1 call - 215 ms'#10 + + '// procedure C - 1 call - 14 ms'#10 + + '// procedure C - 1 call - 11 ms'#10 + + '// procedure D - 1 call - 85 ms'#10 + + '// procedure C - 1 call - 21 ms'#10 + + '// procedure B - 1 call - 35 ms'#10 + + '// procedure B - 1 call - 41 ms'#10 + + '// procedure C - 1 call - 22 ms'#10 + + '//'#10 + + '// NOTE: This option can use a lot of memory. If you enable it, it is recommended'#10 + + '// to limit profiling to the specific code you want to measure by using'#10 + + '// ALCodeProfilerStart and ALCodeProfilerStop.'#10 + + _DefineLine('ALCodeProfilerHistoryGroupNone') + #10 + + #10 + + '// Group calls by procedure ID.'#10 + + '// The result will be displayed as a flat grid, for example:'#10 + + '// procedure A - 1 call - 310 ms'#10 + + '// procedure B - 3 calls - 291 ms'#10 + + '// procedure C - 4 calls - 68 ms'#10 + + '// procedure D - 1 call - 85 ms'#10 + + '//'#10 + + '// NOTE: This is the fastest option and has the lowest impact on each function call.'#10 + + _DefineLine('ALCodeProfilerHistoryGroupByProcID') + #10 + + #10 + + '{$IF defined(ALCodeProfilerHistoryGroupByProcID)}'#10 + + '// Ignore the thread ID. The calls made from every thread are merged together'#10 + + '// instead of producing one row per procedure and per thread, for example:'#10 + + '// procedure A - 3 calls - 310 ms = 1 call from the main thread + 2 calls from a background thread'#10 + + '//'#10 + + '// NOTE: This option is only available with ALCodeProfilerHistoryGroupByProcID.'#10 + + '// All the threads then share the same metrics, which are updated atomically,'#10 + + '// so it slightly increases the cost of each function call, but it also greatly'#10 + + '// reduces the memory usage as only one history is allocated for the whole'#10 + + '// process instead of one per thread.'#10 + + ALIfThenA( + IgnoreThreadIDCheckBox.Checked, + '{$DEFINE ALCodeProfilerIgnoreThreadID}', + '{.$DEFINE ALCodeProfilerIgnoreThreadID}') + #10 + + '{$ENDIF}'#10 + + #10 + + '// Group calls by call stack.'#10 + + '// The result will be displayed as a grouped call tree, for example:'#10 + + '// procedure A - 1 call - 310 ms'#10 + + '// procedure B - 3 calls - 291 ms'#10 + + '// procedure C - 3 calls - 46 ms'#10 + + '// procedure D - 1 call - 85 ms'#10 + + '// procedure C - 1 call - 22 ms'#10 + + _DefineLine('ALCodeProfilerHistoryGroupByCallStack') + #10 + + #10 + + '// Capacity of the history, in number of rows.'#10 + + '//'#10 + + '// NOTE: With ALCodeProfilerHistoryGroupByProcID the history is a flat array'#10 + + '// indexed by the procedure ID, so it only needs to be big enough to hold the'#10 + + '// highest procedure ID of ' + AnsiString(ALCodeProfilerProcIDMapFilename) + '. The value below is'#10 + + '// updated by the Alcinoe Code Profiler GUI each time the markers are inserted'#10 + + '// or removed, so there is no reason to edit it by hand.'#10 + + '{$IF defined(ALCodeProfilerHistoryGroupByProcID)}'#10 + + 'const'#10 + + ' ALCodeProfilerHistoryCapacity = '+ALIntToStrA(GetHistoryCapacity)+';'#10 + + '{$ELSE}'#10 + + 'const'#10 + + ' ALCodeProfilerHistoryCapacity = '+ALIntToStrA(DefaultHistoryCapacity)+'; {1 000 000 * 32 Bytes = 32 MB or with gap = 2 097 152 * 32 Bytes = 67.11 MB}'#10 + + '{$ENDIF}'#10; + ALSaveStringToFile(LContent, LIncFilename); +end; + +{*******************************************} +procedure TMainForm.LoadCodeProfilerIncFile; +begin + var LIncFilename := GetCodeProfilerIncFilename; + var LContent: AnsiString := ''; + if (LIncFilename <> '') and TFile.Exists(LIncFilename) then LContent := ALGetStringFromFile(LIncFilename); + // Populating the controls below raises their OnChange event, which would + // otherwise save these very same values back to Alcinoe.CodeProfiler.inc. + FLoadingSettings := True; + try + //-- + if ALPosA('{$DEFINE ALCodeProfilerHistoryGroupNone}', LContent) > 0 then SelectHistoryGroupMode('ALCodeProfilerHistoryGroupNone') + else if ALPosA('{$DEFINE ALCodeProfilerHistoryGroupByProcID}', LContent) > 0 then SelectHistoryGroupMode('ALCodeProfilerHistoryGroupByProcID') + else SelectHistoryGroupMode('ALCodeProfilerHistoryGroupByCallStack'); + //-- + IgnoreThreadIDCheckBox.Checked := ALPosA('{$DEFINE ALCodeProfilerIgnoreThreadID}', LContent) > 0; + //-- + var LServerName: String := ''; + var LMarker: AnsiString := 'ALCodeProfilerServerName: String = '''; + var LValueStart := ALPosA(LMarker, LContent); + if LValueStart > 0 then begin + inc(LValueStart, Length(LMarker)); + var LValueEnd := ALPosA('''', LContent, LValueStart); + if LValueEnd > LValueStart then + LServerName := String(ALCopyStr(LContent, LValueStart, LValueEnd - LValueStart)); + end; + SelectServerName(LServerName); + //-- + var LEnabled := True; + LMarker := 'ALCodeProfilerEnabled: Boolean = '; + LValueStart := ALPosA(LMarker, LContent); + if LValueStart > 0 then begin + inc(LValueStart, Length(LMarker)); + LEnabled := not ALSameTextA(ALCopyStr(LContent, LValueStart, Length('False')), 'False'); + end; + CodeProfilerEnabledCheckBox.Checked := LEnabled; + finally + FLoadingSettings := False; + end; +end; + +{*********************************} +procedure TMainForm.SaveConfigFile; +begin + var LIniFile := TIniFile.Create(TPath.Combine(FDataDir, ConfigFilename)); + try + LIniFile.WriteString('General','SourcesPath',ALStringReplaceW(ALTrim(SourcesPathMemo.Text), #13#10, ';', [RfReplaceALL])); + LIniFile.WriteString('General','CodeProfilerIncFilename',ALTrim(CodeProfilerIncFilenameEdit.Text)); + finally + ALFreeAndNil(LIniFile); + end; +end; + +{*******************************************************************************} +procedure TMainForm.CodeProfilerIncFilenameEditPropertiesChange(Sender: TObject); +begin + if FLoadingSettings then exit; + SaveConfigFile; + // Reload the options only once the typed path points to an existing file, + // else every intermediate keystroke would reset them to their defaults. + if TFile.Exists(GetCodeProfilerIncFilename) then LoadCodeProfilerIncFile; +end; + +{***************************************************************************} +procedure TMainForm.BrowseCodeProfilerIncFilenameBtnClick(Sender: TObject); +begin + var LOpenDialog := TOpenDialog.Create(nil); + try + LOpenDialog.Title := 'Select Alcinoe.CodeProfiler.inc'; + LOpenDialog.Filter := 'Alcinoe.CodeProfiler.inc|Alcinoe.CodeProfiler.inc|Include files (*.inc)|*.inc|All files (*.*)|*.*'; + LOpenDialog.DefaultExt := 'inc'; + LOpenDialog.Options := LOpenDialog.Options + [ofFileMustExist, ofPathMustExist]; + var LIncFilename := GetCodeProfilerIncFilename; + if LIncFilename <> '' then begin + LOpenDialog.InitialDir := ALExtractFilePath(LIncFilename); + LOpenDialog.FileName := LIncFilename; + end; + if LOpenDialog.Execute(Handle) then + CodeProfilerIncFilenameEdit.Text := LOpenDialog.FileName; + finally + ALFreeAndNil(LOpenDialog); + end; +end; + +{*******************************************************************************} +procedure TMainForm.CodeProfilerEnabledCheckBoxPropertiesChange(Sender: TObject); +begin + SaveCodeProfilerIncFile; end; {*********************************************************} @@ -232,13 +802,11 @@ procedure TMainForm.RemoveMarkers(const AFileName: String); {*****************************************************************} procedure TMainForm.RemoveProfilerMarkersBtnClick(Sender: TObject); begin - if MessageDlg('⚠ WARNING: Make sure to back up your files before continuing! Do you want to continue?', mtWarning, [mbYes, mbCancel], 0) <> mrYes then Exit; var LSourceFilenames := TALStringListW.Create; try ExpandSourcesPath(SourcesPathMemo.Lines, LSourceFilenames); if LSourceFilenames.Count = 0 then Raise Exception.Create('Error: No files have been selected'); - if MessageDlg('Are you REALLY sure you want to update all the files listed below?' + sLineBreak + sLineBreak + LSourceFilenames.Text, mtWarning, [mbYes, mbCancel], 0) <> mrYes then Exit; RemoveProfilerMarkersBtn.Cursor := crHourGlass; Try for var I := 0 to LSourceFilenames.Count - 1 do @@ -246,12 +814,15 @@ procedure TMainForm.RemoveProfilerMarkersBtnClick(Sender: TObject); finally RemoveProfilerMarkersBtn.Cursor := crDefault; End; - var LProcMetricsFilename := TPath.Combine(FDataDir, ALCodeProfilerProcMetricsFilename); + var LProcMetricsFilename := TPath.Combine(FDataDir, GetSelectedProcMetricsFilename); If TFile.Exists(LProcMetricsFilename) then TFile.Delete(LProcMetricsFilename); var LProcIDMapFilename := TPath.Combine(FDataDir, ALCodeProfilerProcIDMapFilename); If TFile.Exists(LProcIDMapFilename) then TFile.Delete(LProcIDMapFilename); + // ALCodeProfilerHistoryCapacity was deduced from the procedure IDs just + // deleted, so Alcinoe.CodeProfiler.inc must be reset as well. + SaveCodeProfilerIncFile; MessageDlg('The operation completed successfully', mtInformation, [mbOK], 0); Finally ALFreeAndNil(LSourceFilenames); @@ -266,8 +837,16 @@ procedure TMainForm.IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: T // We're only handling POST requests here. if ARequestInfo.CommandType = hcPOST then begin - // Define the file path where the POST content will be saved. - var LProcMetricsTmpFilename := TPath.Combine(FDataDir, ALCodeProfilerProcMetricsFilename + '~tmp'); + // Define the file path where the POST content will be saved. The + // filename depends on the currently selected radio button, so it + // must be read on the main thread. + var LProcMetricsFilename: String; + TThread.Synchronize(nil, + procedure + begin + LProcMetricsFilename := TPath.Combine(FDataDir, GetSelectedProcMetricsFilename); + end); + var LProcMetricsTmpFilename := LProcMetricsFilename + '~tmp'; If TFile.Exists(LProcMetricsTmpFilename) then TFile.Delete(LProcMetricsTmpFilename); if Assigned(ARequestInfo.PostStream) then @@ -291,7 +870,6 @@ procedure TMainForm.IdHTTPServerCommandGet(AContext: TIdContext; ARequestInfo: T procedure begin Try - var LProcMetricsFilename := TPath.Combine(FDataDir, ALCodeProfilerProcMetricsFilename); If TFile.Exists(LProcMetricsFilename) then TFile.Delete(LProcMetricsFilename); TFile.Move(LProcMetricsTmpFilename, LProcMetricsFilename); @@ -363,226 +941,256 @@ procedure TMainForm.IdHTTPServerListenException(AThread: TIdListenerThread; AExc procedure TMainForm.InsertMarkers(const AFileName: String; Const AProcIDMap: TALStringListA); type - TProcStackEntry = record - ProcID: cardinal; - ProcName: AnsiString; - ProcIndent: AnsiString; - MarkerAfterBeginAdded: Boolean; + TMarkerInsertion = record + Line: Integer; + Col: Integer; + Text: AnsiString; end; -begin - RemoveMarkers(AFileName); - var LIsDPR := ALSameTextW(ALExtractFileExt(AFileName), '.dpr'); - var LHttpServerNameAdded := False; - var LUnitName := ALExtractFileName(AnsiString(AFileName), true{RemoveFileExt}); - var LUseAdded := False; - var LAnonymousMethodSequence := 0; - var LProcStack := TStack<TProcStackEntry>.Create; - var LSourceCode := TALStringListA.create; - try - LSourceCode.LoadFromFile(AFileName); - var LCurrentProcIndent: AnsiString := ''; - var LAddMarkerAfterNextProcBegin: Boolean := False; - var LAddMarkerBeforeNextProcEnd: Boolean := False; - var LImplementationFound: Boolean := false; - For var I := 0 to LSourceCode.Count - 1 do begin - - var LLine := LSourceCode[i]; - var LTrimedLine := ALTrim(LSourceCode[i]); - - // Handle "uses" - if (not LUseAdded) and - (ALPosIgnoreCaseA(LCurrentProcIndent + 'uses', LLine) = 1) then begin - var LNewLine := LLine; - Insert('{ALCodeProfiler>>}{$DEFINE ALCodeProfiler}Alcinoe.CodeProfiler,{<<ALCodeProfiler}', LNewLine, length(LCurrentProcIndent + 'uses')+1); - LSourceCode[i] := LNewLine; - LUseAdded := True; - Continue; + function IsIdentifierChar(const AChar: AnsiChar): Boolean; + begin + Result := AChar in ['a'..'z', 'A'..'Z', '0'..'9', '_']; + end; + + {*****************************************************} + function IsKeywordAt(const ALine, AKeyword: AnsiString; const ACol: Integer): Boolean; + begin + Result := False; + if ACol < 1 then + Exit; + if ACol + Length(AKeyword) - 1 > Length(ALine) then + Exit; + if not ALSameTextA(ALCopyStr(ALine, ACol, Length(AKeyword)), AKeyword) then + Exit; + if ACol > 1 then + if IsIdentifierChar(ALine[ACol - 1]) then + Exit; + if ACol + Length(AKeyword) <= Length(ALine) then + if IsIdentifierChar(ALine[ACol + Length(AKeyword)]) then + Exit; + Result := True; + end; + + {*************************************************************************} + function FindKeywordColumn(const ALine, AKeyword: AnsiString; const APreferredCol: Integer; const ASearchBackwards: Boolean): Integer; + begin + if IsKeywordAt(ALine, AKeyword, APreferredCol) then + Exit(APreferredCol); + if ASearchBackwards then begin + for var I := Length(ALine) - Length(AKeyword) + 1 downto 1 do + if IsKeywordAt(ALine, AKeyword, I) then + Exit(I); + end + else begin + for var I := 1 to Length(ALine) - Length(AKeyword) + 1 do + if IsKeywordAt(ALine, AKeyword, I) then + Exit(I); + end; + Result := 0; + end; + + {***********************************************************************************} + function FindBeginInsertionColumn(const ASourceCode: TALStringListA; const ALine, ACol: Integer): Integer; + begin + if (ALine < 1) or (ALine > ASourceCode.Count) then + raise Exception.Create('Invalid AST begin line: ' + ALIntToStrW(ALine) + ' - Filename: ' + AFileName); + var LLine := ASourceCode[ALine - 1]; + var LBeginCol := FindKeywordColumn(LLine, 'begin', ACol, False); + if LBeginCol <= 0 then + Exit(0); + Result := LBeginCol + Length('begin'); + end; + + {*********************************************************************************} + function FindEndInsertionColumn(const ASourceCode: TALStringListA; const ALine, ACol: Integer): Integer; + begin + if (ALine < 1) or (ALine > ASourceCode.Count) then + raise Exception.Create('Invalid AST end line: ' + ALIntToStrW(ALine) + ' - Filename: ' + AFileName); + var LLine := ASourceCode[ALine - 1]; + Result := FindKeywordColumn(LLine, 'end', ACol - Length('end'), True); + end; + + {***********************************************************************************************************} + procedure AddInsertion(const AInsertions: TList<TMarkerInsertion>; const ASourceCode: TALStringListA; const ALine, ACol: Integer; const AText: AnsiString); + begin + if (ALine < 1) or (ALine > ASourceCode.Count) then + raise Exception.Create('Invalid insertion line: ' + ALIntToStrW(ALine) + ' - Filename: ' + AFileName); + if (ACol < 1) or (ACol > Length(ASourceCode[ALine - 1]) + 1) then + raise Exception.Create('Invalid insertion column: ' + ALIntToStrW(ACol) + ' - Line: ' + ALIntToStrW(ALine) + ' - Filename: ' + AFileName); + var LInsertion: TMarkerInsertion; + LInsertion.Line := ALine; + LInsertion.Col := ACol; + LInsertion.Text := AText; + AInsertions.Add(LInsertion); + end; + + {*************************************************************************************} + function FindFirstNode(const ANode: TSyntaxNode; const ANodeType: TSyntaxNodeType): TSyntaxNode; + begin + Result := nil; + if not Assigned(ANode) then + Exit; + if ANode.Typ = ANodeType then begin + Result := ANode; + Exit; + end; + for var LChild in ANode.ChildNodes do begin + Result := FindFirstNode(LChild, ANodeType); + if Assigned(Result) then + Exit; + end; + end; + + {****************************************************************} + function GetMethodStatements(const ANode: TSyntaxNode): TCompoundSyntaxNode; + begin + Result := nil; + for var LChild in ANode.ChildNodes do + if (LChild.Typ = ntStatements) and + (LChild is TCompoundSyntaxNode) then begin + Result := TCompoundSyntaxNode(LChild); + Exit; end; + end; - // Handle "begin" in DPR - if (LIsDPR) and - (not LHttpServerNameAdded) and - (ALPosIgnoreCaseA('begin', LLine) = 1) then begin - if (ALTrim(HttpServerNameEdit.Text) <> '') and - (ALTrim(HttpServerPortEdit.Text) <> '') then begin - var LNewLine := LLine; - Insert('{ALCodeProfiler>>}ALCodeProfilerServerName := ''http://'+ALTrim(AnsiString(HttpServerNameEdit.Text))+':'+ALTrim(AnsiString(HttpServerPortEdit.Text))+''';{<<ALCodeProfiler}', LNewLine, length('begin')+1); - LSourceCode[i] := LNewLine; + {**********************************************************************************************} + function GetMethodName(const ANode: TSyntaxNode; var AAnonymousMethodSequence: Integer): AnsiString; + begin + Result := ALTrim(AnsiString(ANode.GetAttribute(anName))); + for var LChild in ANode.ChildNodes do + if (LChild.Typ = ntName) and + (LChild is TValuedSyntaxNode) then begin + var LNamePart := ALTrim(AnsiString(TValuedSyntaxNode(LChild).Value)); + if (LNamePart <> '') and + ((Result = '') or (ALPosIgnoreCaseA(LNamePart + '.', Result) <> 1)) then begin + if Result <> '' then + Result := LNamePart + '.' + Result + else + Result := LNamePart; end; - LHttpServerNameAdded := True; - Continue; end; + if Result = '' then begin + inc(AAnonymousMethodSequence); + Result := '$AnonymousMethod' + ALIntToStrA(AAnonymousMethodSequence); + end; + end; - // Ignore Interface section - if ALSameTextA(LTrimedLine, 'implementation') then begin - LImplementationFound := True; - continue; - end; - if not LImplementationFound then continue; - - // Ignore lines like: - // - // TALDynamicListBox = class(TALControl) - // public - // procedure Prepare; virtual; - // end; - if (LAddMarkerAfterNextProcBegin) and - (LTrimedLine <> '') and - (LCurrentProcIndent <> '') and - (ALPosIgnoreCaseA(LCurrentProcIndent, LLine) <> 1) then begin - LProcStack.Pop; - LCurrentProcIndent := ''; - LAddMarkerAfterNextProcBegin := False; - LAddMarkerBeforeNextProcEnd := False; + {*********************************************************************************************************************************************************************} + procedure CollectMethodMarkers(const ANode: TSyntaxNode; const AParentProcName: AnsiString; const AUnitName: AnsiString; const ASourceCode: TALStringListA; const AInsertions: TList<TMarkerInsertion>; var AAnonymousMethodSequence: Integer); + begin + if not Assigned(ANode) then + Exit; + + var LParentProcName := AParentProcName; + if ANode.Typ in [ntMethod, ntAnonymousMethod] then begin + var LProcName := GetMethodName(ANode, AAnonymousMethodSequence); + if LParentProcName <> '' then + LProcName := LParentProcName + '.' + LProcName; + LParentProcName := LProcName; + + var LStatements := GetMethodStatements(ANode); + if Assigned(LStatements) then begin + var LBeginInsertionCol := FindBeginInsertionColumn(ASourceCode, LStatements.Line, LStatements.Col); + var LEndInsertionCol := FindEndInsertionColumn(ASourceCode, LStatements.EndLine, LStatements.EndCol); + if (LBeginInsertionCol > 0) and (LEndInsertionCol > 0) then begin + inc(FProcIDSequence); + AddInsertion( + AInsertions, + ASourceCode, + LStatements.Line, + LBeginInsertionCol, + '{ALCodeProfiler>>}ALCodeProfilerEnterProc('+ALIntToStrA(FProcIDSequence)+'); try{<<ALCodeProfiler}'); + AddInsertion( + AInsertions, + ASourceCode, + LStatements.EndLine, + LEndInsertionCol, + '{ALCodeProfiler>>}finally ALCodeProfilerExitProc('+ALIntToStrA(FProcIDSequence)+'); end;{<<ALCodeProfiler}'); + AProcIDMap.Add(ALIntToStrA(FProcIDSequence) + '=' + AUnitName + '.' + LProcName); + end; end; + end; - // Add procedure like: - // - // Procedure ALDynamicListBoxMakeBufDrawables(const AControl: TALDynamicControl; const AEnsureDoubleBuffered: Boolean = True); - // begin - // end - // - // constructor TALDynamicControl.Create(const AOwner: TObject); - // begin - // end - // - // TThread.queue(nil, - // procedure - // begin - // ... - // end); - // - // TThread.CreateAnonymousThread( - // procedure - // begin - // end).Start; - If (ALPosIgnoreCaseA('class function ', LTrimedLine) = 1) or - (ALPosIgnoreCaseA('class procedure ', LTrimedLine) = 1) or - (ALPosIgnoreCaseA('class operator ', LTrimedLine) = 1) or - (ALPosIgnoreCaseA('constructor ', LTrimedLine) = 1) or - (ALPosIgnoreCaseA('destructor ', LTrimedLine) = 1) or - (ALPosIgnoreCaseA('function', LTrimedLine) = 1) or - (ALPosIgnoreCaseA('procedure', LTrimedLine) = 1) then begin - var J := 1; - While (J <= High(LLine)) and (LLine[j] = ' ') do inc(J); - var LProcIndent := ALCopyStr(LLine, 1, J-1); - // Ignore lines like: - // function PMSessionValidatePrintSettings: OSStatus; cdecl; external libPrintCore name '_PMSessionValidatePrintSettings'; - // function PMSessionSetDestination: OSStatus; cdecl; external libPrintCore name '_PMSessionSetDestination'; - If (LAddMarkerAfterNextProcBegin) and (LProcIndent = LCurrentProcIndent) then - LProcStack.Clear; - LCurrentProcIndent := LProcIndent; - var LProcName: AnsiString; - If (ALPosIgnoreCaseA('class function ', LTrimedLine) = 1) then LProcName := ALStringReplaceA(LTrimedLine,'class function', '', [rfIgnoreCase]) - else If (ALPosIgnoreCaseA('class procedure ', LTrimedLine) = 1) then LProcName := ALStringReplaceA(LTrimedLine,'class procedure', '', [rfIgnoreCase]) - else If (ALPosIgnoreCaseA('class operator ', LTrimedLine) = 1) then LProcName := ALStringReplaceA(LTrimedLine,'class operator', '', [rfIgnoreCase]) - else If (ALPosIgnoreCaseA('constructor ', LTrimedLine) = 1) then LProcName := ALStringReplaceA(LTrimedLine,'constructor ', '', [rfIgnoreCase]) - else If (ALPosIgnoreCaseA('destructor ', LTrimedLine) = 1) then LProcName := ALStringReplaceA(LTrimedLine,'destructor ', '', [rfIgnoreCase]) - else If (ALPosIgnoreCaseA('function', LTrimedLine) = 1) then LProcName := ALStringReplaceA(LTrimedLine,'function', '', [rfIgnoreCase]) - else If (ALPosIgnoreCaseA('procedure', LTrimedLine) = 1) then LProcName := ALStringReplaceA(LTrimedLine,'procedure', '', [rfIgnoreCase]) - else Raise Exception.Create('Error C67E3412-6E90-4317-89A5-EED1E0A496B2'); - LProcName := ALTrim(LProcName); - J := 1; - While true do begin - While (J <= High(LProcName)) and (LProcName[j] in ['a'..'z','A'..'Z','0'..'9','_','.']) do inc(J); - If (J <= High(LProcName)) and (LProcName[j] = '<') then - While (J <= High(LProcName)) and (LProcName[j] <> '>') do inc(J) + for var LChild in ANode.ChildNodes do + CollectMethodMarkers(LChild, LParentProcName, AUnitName, ASourceCode, AInsertions, AAnonymousMethodSequence); + end; + + {*****************************************************************************************************} + procedure ApplyInsertions(const ASourceCode: TALStringListA; const AInsertions: TList<TMarkerInsertion>); + begin + AInsertions.Sort( + TComparer<TMarkerInsertion>.Construct( + function(const Left, Right: TMarkerInsertion): Integer + begin + if Left.Line <> Right.Line then + Result := Right.Line - Left.Line else - break; - inc(J); - end; - LProcName := ALCopyStr(LProcName, 1, J-1); - If LProcName = '' then begin - inc(LAnonymousMethodSequence); - LProcName := '$AnonymousMethod' + ALIntToStrA(LAnonymousMethodSequence); - end - else LAnonymousMethodSequence := 0; - if LProcStack.Count > 0 then - LProcName := LProcStack.Peek.ProcName + '.' + LProcName; - inc(FProcIDSequence); - var LProcStackEntry: TProcStackEntry; - LProcStackEntry.ProcID := FProcIDSequence; - LProcStackEntry.ProcName := LProcName; - LProcStackEntry.ProcIndent := LCurrentProcIndent; - LProcStackEntry.MarkerAfterBeginAdded := False; - LProcStack.Push(LProcStackEntry); - LAddMarkerAfterNextProcBegin := True; - LAddMarkerBeforeNextProcEnd := False; - end + Result := Right.Col - Left.Col; + end)); - // Handle "begin" - else if (LAddMarkerAfterNextProcBegin) and - (ALPosIgnoreCaseA(LCurrentProcIndent + 'begin', LLine) = 1) then begin - if LProcStack.Count = 0 then - raise Exception.Create( - 'The source code is not properly formatted. '+ - 'CodeProfiler requires all procedures to be perfectly '+ - 'indented to function correctly - ' + - 'Line: ' + ALIntToStrW(I+1) + ' - ' + - 'Filename: ' + AFileName + ' - ' + - 'Error: 75F32B58-8284-493D-BE75-2F9F3DE2DEF4'); - var LNewLine := LLine; - Insert('{ALCodeProfiler>>}ALCodeProfilerEnterProc('+ALIntToStrA(LProcStack.Peek.ProcID){$IF defined(debug)}+'{ '+LProcStack.Peek.ProcName+' }'{$ENDIF}+'); try{<<ALCodeProfiler}', LNewLine, length(LCurrentProcIndent + 'begin')+1); - LSourceCode[i] := LNewLine; - var LProcStackEntry := LProcStack.Peek; - LProcStackEntry.MarkerAfterBeginAdded := True; - LProcStack.Pop; - LProcStack.Push(LProcStackEntry); - LAddMarkerAfterNextProcBegin := False; - LAddMarkerBeforeNextProcEnd := True; - end + for var LInsertion in AInsertions do begin + var LLine := ASourceCode[LInsertion.Line - 1]; + Insert(LInsertion.Text, LLine, LInsertion.Col); + ASourceCode[LInsertion.Line - 1] := LLine; + end; + end; - // Handle "end" - else if (LAddMarkerBeforeNextProcEnd) and - (ALPosIgnoreCaseA(LCurrentProcIndent + 'end', LLine) = 1) then begin - if LProcStack.Count = 0 then - raise Exception.Create( - 'The source code is not properly formatted. '+ - 'CodeProfiler requires all procedures to be perfectly '+ - 'indented to function correctly - ' + - 'Line: ' + ALIntToStrW(I+1) + ' - ' + - 'Filename: ' + AFileName + ' - ' + - 'Error: 469093AA-2081-4271-97B9-4218B1E88C83'); - var LNewLine := LLine; - Insert('{ALCodeProfiler>>}finally ALCodeProfilerExitProc('+ALIntToStrA(LProcStack.Peek.ProcID){$IF defined(debug)}+'{ '+LProcStack.Peek.ProcName+' }'{$ENDIF}+'); end;{<<ALCodeProfiler}', LNewLine, length(LCurrentProcIndent)+1); - LSourceCode[i] := LNewLine; - AProcIDMap.Add(ALIntToStrA(LProcStack.Peek.ProcId) + '=' + LUnitName + '.' + LProcStack.Peek.ProcName); - LProcStack.Pop; - if LProcStack.Count > 0 then begin - LCurrentProcIndent := LProcStack.Peek.ProcIndent; - LAddMarkerAfterNextProcBegin := not LProcStack.Peek.MarkerAfterBeginAdded; - LAddMarkerBeforeNextProcEnd := LProcStack.Peek.MarkerAfterBeginAdded; - end - else begin - LAddMarkerAfterNextProcBegin := False; - LAddMarkerBeforeNextProcEnd := False; - end; - end; +begin + RemoveMarkers(AFileName); + var LIsDPR := ALSameTextW(ALExtractFileExt(AFileName), '.dpr'); + var LUnitName := ALExtractFileName(AnsiString(AFileName), true{RemoveFileExt}); + var LSyntaxTree := TPasSyntaxTreeBuilder.Run(AFileName); + var LInsertions := TList<TMarkerInsertion>.Create; + var LSourceCode := TALStringListA.create; + try + LSourceCode.LoadFromFile(AFileName); + var LUsesNode := FindFirstNode(LSyntaxTree, ntUses); + if Assigned(LUsesNode) then + AddInsertion( + LInsertions, + LSourceCode, + LUsesNode.Line, + LUsesNode.Col + Length('uses'), + '{ALCodeProfiler>>}{$DEFINE ALCodeProfiler}Alcinoe.CodeProfiler,{<<ALCodeProfiler}') + else if not LIsDPR then begin + var LInterfaceNode := FindFirstNode(LSyntaxTree, ntInterface); + if not Assigned(LInterfaceNode) then + raise Exception.Create('Interface section not found - Filename: ' + AFileName); + var LInterfaceCol := FindKeywordColumn(LSourceCode[LInterfaceNode.Line - 1], 'interface', LInterfaceNode.Col, False); + if LInterfaceCol <= 0 then + raise Exception.Create('Interface keyword not found at line ' + ALIntToStrW(LInterfaceNode.Line) + ' - Filename: ' + AFileName); + AddInsertion( + LInsertions, + LSourceCode, + LInterfaceNode.Line, + LInterfaceCol + Length('interface'), + '{ALCodeProfiler>>}{$DEFINE ALCodeProfiler}uses Alcinoe.CodeProfiler;{<<ALCodeProfiler}'); end; + if not LIsDPR then begin + var LImplementationNode := FindFirstNode(LSyntaxTree, ntImplementation); + if Assigned(LImplementationNode) then begin + var LAnonymousMethodSequence := 0; + CollectMethodMarkers(LImplementationNode, '', LUnitName, LSourceCode, LInsertions, LAnonymousMethodSequence); + end; + end; + + ApplyInsertions(LSourceCode, LInsertions); LSourceCode.ProtectedSave := true; LSourceCode.SaveToFile(AFileName); finally AlFreeAndNil(LSourceCode); - ALFreeAndNil(LProcStack); + ALFreeAndNil(LInsertions); + ALFreeAndNil(LSyntaxTree); end; end; {*****************************************************************} procedure TMainForm.InsertProfilerMarkersBtnClick(Sender: TObject); begin - var LIniFile := TIniFile.Create(TPath.Combine(FDataDir, ConfigFilename)); - try - LIniFile.WriteString('General','ServerName',HttpServerNameEdit.Text); - LIniFile.WriteString('General','ServerPort',HttpServerPortEdit.Text); - LIniFile.WriteString('General','SourcesPath',ALStringReplaceW(ALTrim(SourcesPathMemo.Text), #13#10, ';', [RfReplaceALL])); - finally - ALFreeAndNil(LIniFile); - end; - if MessageDlg('⚠ WARNING: Make sure to back up your files before continuing! Do you want to continue?', mtWarning, [mbYes, mbCancel], 0) <> mrYes then Exit; + SaveConfigFile; var LSourceFilenames := TALStringListW.Create; var LFailedFilenames := TALStringListW.Create; var LProcIDMap := TALStringListA.Create; @@ -590,7 +1198,29 @@ procedure TMainForm.InsertProfilerMarkersBtnClick(Sender: TObject); ExpandSourcesPath(SourcesPathMemo.Lines, LSourceFilenames); if LSourceFilenames.Count = 0 then Raise Exception.Create('Error: No files have been selected'); - if MessageDlg('Are you REALLY sure you want to update all the files listed below?' + sLineBreak + sLineBreak + LSourceFilenames.Text, mtWarning, [mbYes, mbCancel], 0) <> mrYes then Exit; + + // Ask whether the previously collected data must be cleared. If it is + // kept, preload the existing proc ID map and continue the ID sequence + // after the highest existing ProcID so that the IDs already present in + // the sources and in the performance file stay valid + var LProcIDMapFilename := TPath.Combine(FDataDir, ALCodeProfilerProcIDMapFilename); + var LProcMetricsFilenameOnly := GetSelectedProcMetricsFilename; + var LClearData := MessageDlg( + 'Do you want to start a fresh profiling session and clear the data collected so far?'+ sLineBreak + sLineBreak + + 'YES – Start fresh: the procedure IDs ('+ALCodeProfilerProcIDMapFilename+') and the collected performance data ('+LProcMetricsFilenameOnly+') will be discarded, and the ID numbering will restart from 1. '+ + 'Choose this only if the selected files cover ALL the files currently containing profiler markers; any file left instrumented from a previous run would keep old IDs that clash with the new ones.'+ sLineBreak + sLineBreak + + 'NO – Keep the existing data: the procedures of the selected files will be assigned new IDs, following the existing ones, so previously instrumented files and already collected performance data stay valid.', + mtConfirmation, [mbYes, mbNo, mbCancel], 0); + if (LClearData <> mrYes) and (LClearData <> mrNo) then Exit; + if LClearData = mrYes then FProcIDSequence := 0 + else if TFile.Exists(LProcIDMapFilename) then begin + LProcIDMap.LoadFromFile(LProcIDMapFilename); + for var I := 0 to LProcIDMap.Count - 1 do begin + var LProcID := ALStrToInt(LProcIDMap.Names[I]); + if LProcID > FProcIDSequence then FProcIDSequence := LProcID; + end; + end; + InsertProfilerMarkersBtn.Cursor := crHourGlass; Try for var I := 0 to LSourceFilenames.Count - 1 do @@ -600,15 +1230,26 @@ procedure TMainForm.InsertProfilerMarkersBtnClick(Sender: TObject); On E: Exception do LFailedFilenames.Add(LSourceFilenames[i]); end; - LProcIDMap.SaveToFile(TPath.Combine(FDataDir, ALCodeProfilerProcIDMapFilename)); + LProcIDMap.SaveToFile(LProcIDMapFilename); + // ALCodeProfilerHistoryCapacity depends on the procedure IDs just + // assigned, so Alcinoe.CodeProfiler.inc must be updated as well. + SaveCodeProfilerIncFile; finally InsertProfilerMarkersBtn.Cursor := crDefault; End; - var LProcMetricsFilename := TPath.Combine(FDataDir, ALCodeProfilerProcMetricsFilename); - If TFile.Exists(LProcMetricsFilename) then - TFile.Delete(LProcMetricsFilename); + if LClearData = mrYes then begin + var LProcMetricsFilename := TPath.Combine(FDataDir, LProcMetricsFilenameOnly); + If TFile.Exists(LProcMetricsFilename) then + TFile.Delete(LProcMetricsFilename); + end; if LFailedFilenames.Count > 0 then - MessageDlg('The operation completed successfully, except for the following file(s), which are badly formatted and could not be updated:' + sLineBreak + LFailedFilenames.Text, mtError, [mbOK], 0) + MessageDlg( + 'The operation completed except for the following file(s), which are '+ + 'badly formatted and could not be updated. Now, you must recompile your '+ + 'project and run it. After you close the application (or move it '+ + 'between background and foreground on Android/iOS), a performance '+ + 'file (ALCodeProfilerProcMetrics.dat) will be generated in the user''s '+ + 'document folder.' + sLineBreak + sLineBreak + LFailedFilenames.Text, mtError, [mbOK], 0) else MessageDlg( 'The operation completed successfully. Now, you must recompile your '+ @@ -632,38 +1273,83 @@ procedure TMainForm.Refresh; try GridTableViewProcMetrics.DataController.RecordCount := 0; for var I := low(FProcMetrics) to high(FProcMetrics) do begin - if FOverrideFilterParentExecutionID <> 0 then begin - if FProcMetrics[i].ParentExecutionID <> FOverrideFilterParentExecutionID then - continue; - //-- - if (FFilterStartTimeStampMin > 0) and - (FProcMetrics[i].StartTimeStamp + FProcMetrics[i].ElapsedTicks < FFilterStartTimeStampMin) then - continue; - //-- - if (FFilterStartTimeStampMax > 0) and - (FProcMetrics[i].StartTimeStamp > FFilterStartTimeStampMax) then - continue; - end - else begin - if (FFilterParentExecutionIDs.Count > 0) and - (not FFilterParentExecutionIDs.Contains(FProcMetrics[i].ParentExecutionID)) then - continue; - //-- - if (FFilterExecutionIDs.Count > 0) and - (not FFilterExecutionIDs.Contains(FProcMetrics[i].ExecutionID)) then - continue; - //-- - if (FFilterProcIDs.Count > 0) and - (not FFilterProcIDs.Contains(FProcMetrics[i].ProcID)) then - continue; + case FHistoryGroupMode of + hgmNone: begin + if FOverrideFilterParentExecutionID <> 0 then begin + if FProcMetrics[i].ParentExecutionID <> FOverrideFilterParentExecutionID then + continue; + //-- + if (FOverrideFilterThreadID <> High(Cardinal)) and + (FProcMetrics[i].ThreadID <> FOverrideFilterThreadID) then + continue; + //-- + if (FFilterStartTimeStampMin > 0) and + (FProcMetrics[i].StartTimeStamp + FProcMetrics[i].ElapsedTicks < FFilterStartTimeStampMin) then + continue; + //-- + if (FFilterStartTimeStampMax > 0) and + (FProcMetrics[i].StartTimeStamp > FFilterStartTimeStampMax) then + continue; + end + else begin + if (FFilterParentExecutionIDs.Count > 0) and + (not FFilterParentExecutionIDs.Contains(FProcMetrics[i].ParentExecutionID)) then + continue; + //-- + if (FFilterExecutionIDs.Count > 0) and + (not FFilterExecutionIDs.Contains(FProcMetrics[i].ExecutionID)) then + continue; + //-- + if (FFilterProcIDs.Count > 0) and + (not FFilterProcIDs.Contains(FProcMetrics[i].ProcID)) then + continue; + end; + end; + hgmByCallStack: begin + if FOverrideFilterParentExecutionID <> 0 then begin + if FProcMetrics[i].ParentMetricsID <> FOverrideFilterParentExecutionID then + continue; + //-- + if (FOverrideFilterThreadID <> High(Cardinal)) and + (FProcMetrics[i].ThreadID <> FOverrideFilterThreadID) then + continue; + end + else begin + if (FFilterParentExecutionIDs.Count > 0) and + (not FFilterParentExecutionIDs.Contains(FProcMetrics[i].ParentMetricsID)) then + continue; + //-- + if (FFilterProcIDs.Count > 0) and + (not FFilterProcIDs.Contains(FProcMetrics[i].ProcID)) then + continue; + end; + end; + hgmByProcID: begin + if (FFilterProcIDs.Count > 0) and + (not FFilterProcIDs.Contains(FProcMetrics[i].ProcID)) then + continue; + end; end; var LRecordCount := GridTableViewProcMetrics.DataController.RecordCount; inc(LRecordCount); GridTableViewProcMetrics.DataController.RecordCount := LRecordCount; - GridTableViewProcMetrics.DataController.SetValue(LRecordCount-1, GridTableViewProcMetricsColumnExecutionID.Index, FProcMetrics[i].ExecutionID); + // No per-call ExecutionID exists in grouped modes, so a mode-specific + // surrogate is reused as the row identity: ProcID is unique enough in + // hgmByProcID (there is only ever one row per ProcID), but in + // hgmByCallStack the same ProcID can appear under different + // parents, so MetricsID (unique per row, and also the drill-down key + // matched against ParentMetricsID above) must be used instead. + case FHistoryGroupMode of + hgmNone: GridTableViewProcMetrics.DataController.SetValue(LRecordCount-1, GridTableViewProcMetricsColumnExecutionID.Index, FProcMetrics[i].ExecutionID); + hgmByProcID: GridTableViewProcMetrics.DataController.SetValue(LRecordCount-1, GridTableViewProcMetricsColumnExecutionID.Index, FProcMetrics[i].ProcID); + hgmByCallStack: GridTableViewProcMetrics.DataController.SetValue(LRecordCount-1, GridTableViewProcMetricsColumnExecutionID.Index, FProcMetrics[i].MetricsID); + end; GridTableViewProcMetrics.DataController.SetValue(LRecordCount-1, GridTableViewProcMetricsColumnThreadID.Index, FProcMetrics[i].ThreadID); GridTableViewProcMetrics.DataController.SetValue(LRecordCount-1, GridTableViewProcMetricsColumnProcName.Index, String(FProcIDMap.Values[ALIntToStrA(FProcMetrics[i].ProcID)])); - GridTableViewProcMetrics.DataController.SetValue(LRecordCount-1, GridTableViewProcMetricsColumnStartTimeStamp.Index, FProcMetrics[i].StartTimeStamp * ALCodeProfilerMillisecondsPerTick); + if FHistoryGroupMode = hgmNone then + GridTableViewProcMetrics.DataController.SetValue(LRecordCount-1, GridTableViewProcMetricsColumnStartTimestamp.Index, FProcMetrics[i].StartTimeStamp * ALCodeProfilerMillisecondsPerTick) + else + GridTableViewProcMetrics.DataController.SetValue(LRecordCount-1, GridTableViewProcMetricsColumnCallCount.Index, FProcMetrics[i].CallCount); GridTableViewProcMetrics.DataController.SetValue(LRecordCount-1, GridTableViewProcMetricsColumnTimeTaken.Index, FProcMetrics[i].ElapsedTicks * ALCodeProfilerMillisecondsPerTick); end; finally @@ -675,37 +1361,63 @@ procedure TMainForm.Refresh; {****************************************************} procedure TMainForm.LoadDataBtnClick(Sender: TObject); begin - var LProcMetricsFilename := TPath.Combine(FDataDir, ALCodeProfilerProcMetricsFilename); + FHistoryGroupMode := GetSelectedHistoryGroupModeEnum; + var LProcMetricsFilenameOnly := GetSelectedProcMetricsFilename; + var LProcMetricsFilename := TPath.Combine(FDataDir, LProcMetricsFilenameOnly); If not TFile.Exists(LProcMetricsFilename) then raise Exception.CreateFmt( 'The required file "%s" is missing. Please make '+ 'sure it is available in the data subfolder where '+ 'Alcinoe Code Profiler is located before proceeding.', - [ALCodeProfilerProcMetricsFilename]); + [LProcMetricsFilenameOnly]); LoadDataBtn.Cursor := crHourGlass; Try // Load ALCodeProfilerProcMetrics.dat in FProcMetrics + var LRawRecordSize := GetProcMetricsRawRecordSize(FHistoryGroupMode); Var LfileStream := TfileStream.Create(LProcMetricsFilename, fmOpenRead); try - setlength(FProcMetrics, LfileStream.Size div SizeOf(TALProcMetrics)); - if length(FProcMetrics) > 0 then - LfileStream.ReadBuffer(FProcMetrics[0], length(FProcMetrics) * SizeOf(TALProcMetrics)); + if LfileStream.Size mod LRawRecordSize <> 0 then + raise Exception.CreateFmt('The file "%s" is corrupted', [LProcMetricsFilenameOnly]); + var LRawBytes: TBytes; + SetLength(LRawBytes, LfileStream.Size); + if Length(LRawBytes) > 0 then + LfileStream.ReadBuffer(LRawBytes[0], Length(LRawBytes)); + setlength(FProcMetrics, Length(LRawBytes) div LRawRecordSize); + for var I := low(FProcMetrics) to high(FProcMetrics) do + DecodeProcMetricsRaw(FHistoryGroupMode, LRawBytes, I * LRawRecordSize, FProcMetrics[i]); finally ALFreeandNil(LfileStream); end; // Update orphan node - Var LDictionary := TDictionary<Cardinal, boolean>.Create; - try - For var I := low(FProcMetrics) to High(FProcMetrics) do - LDictionary.Add(FProcMetrics[i].ExecutionID, true); - var LBool: Boolean; - For var I := low(FProcMetrics) to High(FProcMetrics) do - if not LDictionary.TryGetValue(FProcMetrics[i].ParentExecutionID, LBool) then - FProcMetrics[i].ParentExecutionID := 0; - finally - AlFreeAndNil(LDictionary); + case FHistoryGroupMode of + hgmNone: begin + Var LDictionary := TDictionary<Cardinal, boolean>.Create; + try + For var I := low(FProcMetrics) to High(FProcMetrics) do + LDictionary.Add(FProcMetrics[i].ExecutionID, true); + var LBool: Boolean; + For var I := low(FProcMetrics) to High(FProcMetrics) do + if not LDictionary.TryGetValue(FProcMetrics[i].ParentExecutionID, LBool) then + FProcMetrics[i].ParentExecutionID := 0; + finally + AlFreeAndNil(LDictionary); + end; + end; + hgmByCallStack: begin + Var LDictionary := TDictionary<Cardinal, boolean>.Create; + try + For var I := low(FProcMetrics) to High(FProcMetrics) do + LDictionary.AddOrSetValue(FProcMetrics[i].MetricsID, true); + var LBool: Boolean; + For var I := low(FProcMetrics) to High(FProcMetrics) do + if not LDictionary.TryGetValue(FProcMetrics[i].ParentMetricsID, LBool) then + FProcMetrics[i].ParentMetricsID := 0; + finally + AlFreeAndNil(LDictionary); + end; + end; end; // Load ALCodeProfilerProcIDMap.txt in FProcIDMap @@ -720,8 +1432,7 @@ procedure TMainForm.LoadDataBtnClick(Sender: TObject); FFilterParentExecutionIDs.Add(0); // Reset TreeListProcMetrics - FTreeListProcMetricsTailNode := nil; - TreeListProcMetrics.Clear; + ResetTreeListProcMetrics; FGoBackStack.Clear; // Reset the grid @@ -737,6 +1448,66 @@ procedure TMainForm.LoadDataBtnClick(Sender: TObject); MessageDlg(ALIntToStrW(Length(FProcMetrics)) + ' records have been loaded successfully.', mtInformation, [mbOK], 0); end; +{*****************************************************} +procedure TMainForm.ClearDataBtnClick(Sender: TObject); +begin + if MessageDlg( + 'Do you want to delete all the performance data collected so far? ' + + 'Every performance file of the CodeProfiler data folder will be deleted and the grid will be emptied. ' + + 'The procedure IDs (' + ALCodeProfilerProcIDMapFilename + ') are kept, so the sources already instrumented stay valid ' + + 'and the next run will simply collect fresh data.', + mtConfirmation, [mbYes, mbNo], 0) <> mrYes then exit; + + var LDeletedCount := 0; + ClearDataBtn.Cursor := crHourGlass; + try + + // Delete every performance file, whatever the history group mode they + // were collected with. This must not run while the HTTP server is + // receiving a new performance file in FDataDir. + FHttpServerCriticalSection.Acquire; + try + if TDirectory.Exists(FDataDir) then begin + var LFilenames := TDirectory.GetFiles(FDataDir, '*', TSearchOption.soTopDirectoryOnly); + for var I := low(LFilenames) to high(LFilenames) do begin + // '.dat~tmp' is the temporary file the HTTP server writes the + // incoming performance file to before renaming it to '.dat'. + var LExtension := TPath.GetExtension(LFilenames[I]); + if (not ALSameTextW(LExtension, '.dat')) and + (not ALSameTextW(LExtension, '.dat~tmp')) then continue; + TFile.Delete(LFilenames[I]); + inc(LDeletedCount); + end; + end; + finally + FHttpServerCriticalSection.Release; + end; + + // Drop the data loaded in memory + setlength(FProcMetrics, 0); + FProcIDMap.Clear; + + // Reset Filter + ResetFilters; + FFilterParentExecutionIDs.Add(0); + + // Reset TreeListProcMetrics + ResetTreeListProcMetrics; + FGoBackStack.Clear; + + // Empty the grid + Refresh; + + // Clear the StatusBar + MainStatusBar.Panels[1].Text := ''; + + finally + ClearDataBtn.Cursor := crDefault; + end; + + MessageDlg(ALIntToStrW(LDeletedCount) + ' performance file(s) have been deleted successfully.', mtInformation, [mbOK], 0); +end; + {*****************************************************} procedure TMainForm.PanelfilterResize(Sender: TObject); begin @@ -749,14 +1520,205 @@ procedure TMainForm.InstrumentationTabSheetResize(Sender: TObject); InstructionPanel.Height := LastInstructionLabel.Top + LastInstructionLabel.Height + LastInstructionLabel.Margins.Bottom; end; +{*******************************************************} +procedure TMainForm.ExportToCsvBtnClick(Sender: TObject); + +var + LCsvStream: TFileStream; + LCsvBuffer: AnsiString; + LCsvBufferPos: Integer; + + {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~} + procedure _WriteToCsvBuffer(const AStr: AnsiString); + begin + if LCsvBufferPos + length(AStr) > length(LCsvBuffer) then begin + LCsvStream.WriteBuffer(PAnsiChar(LCsvBuffer)^, LCsvBufferPos); + LCsvBufferPos := 0; + end; + ALMove(PAnsiChar(AStr)^, LCsvBuffer[LCsvBufferPos + 1], length(AStr)); + LCsvBufferPos := LCsvBufferPos + length(AStr); + end; + + {~~~~~~~~~~~~~~~~~~~~~~} + procedure _FlushCsvBuffer; + begin + if LCsvBufferPos > 0 then begin + LCsvStream.WriteBuffer(PAnsiChar(LCsvBuffer)^, LCsvBufferPos); + LCsvBufferPos := 0; + end; + end; + + {~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~} + procedure _AppendToCsvRow(var ARow: AnsiString; var AFirstColumn: Boolean; const AValue: AnsiString); + begin + if not AFirstColumn then ARow := ARow + ','; + AFirstColumn := False; + ARow := ARow + AValue; + end; + +begin + var LHistoryGroupMode := GetSelectedHistoryGroupModeEnum; + var LRawRecordSize := GetProcMetricsRawRecordSize(LHistoryGroupMode); + var LColumns := GetProcMetricsColumnsForMode(LHistoryGroupMode); + var LProcMetricsFilenameOnly := GetSelectedProcMetricsFilename; + var LProcMetricsFilename := TPath.Combine(FDataDir, LProcMetricsFilenameOnly); + If not TFile.Exists(LProcMetricsFilename) then + raise Exception.CreateFmt( + 'The required file "%s" is missing. Please make '+ + 'sure it is available in the data subfolder where '+ + 'Alcinoe Code Profiler is located before proceeding.', + [LProcMetricsFilenameOnly]); + + // Ask which columns to export + var LExportColumns: TArray<Boolean>; + SetLength(LExportColumns, Length(LColumns)); + var LColumnsForm := TForm.CreateNew(nil); + try + LColumnsForm.Caption := 'Export to CSV'; + LColumnsForm.BorderStyle := bsDialog; + LColumnsForm.Position := poScreenCenter; + LColumnsForm.ClientWidth := 300; + LColumnsForm.ClientHeight := 233; + var LColumnsLabel := TLabel.Create(LColumnsForm); + LColumnsLabel.Parent := LColumnsForm; + LColumnsLabel.Caption := 'Select the columns to export:'; + LColumnsLabel.SetBounds(8, 8, LColumnsForm.ClientWidth - 16, 15); + var LColumnsCheckListBox := TCheckListBox.Create(LColumnsForm); + LColumnsCheckListBox.Parent := LColumnsForm; + LColumnsCheckListBox.SetBounds(8, 29, LColumnsForm.ClientWidth - 16, 161); + for var I := 0 to High(LColumns) do begin + LColumnsCheckListBox.Items.Add(String(GetProcMetricsColumnName(LColumns[I]))); + LColumnsCheckListBox.Checked[I] := True; + end; + var LOkBtn := TButton.Create(LColumnsForm); + LOkBtn.Parent := LColumnsForm; + LOkBtn.Caption := 'OK'; + LOkBtn.ModalResult := mrOk; + LOkBtn.Default := True; + LOkBtn.SetBounds(LColumnsForm.ClientWidth - 170, 198, 75, 27); + var LCancelBtn := TButton.Create(LColumnsForm); + LCancelBtn.Parent := LColumnsForm; + LCancelBtn.Caption := 'Cancel'; + LCancelBtn.ModalResult := mrCancel; + LCancelBtn.Cancel := True; + LCancelBtn.SetBounds(LColumnsForm.ClientWidth - 87, 198, 75, 27); + if LColumnsForm.ShowModal <> mrOk then exit; + for var I := 0 to High(LColumns) do + LExportColumns[I] := LColumnsCheckListBox.Checked[I]; + finally + ALFreeAndNil(LColumnsForm); + end; + var LExportAnyColumn := False; + for var I := 0 to High(LColumns) do + LExportAnyColumn := LExportAnyColumn or LExportColumns[I]; + if not LExportAnyColumn then + Raise Exception.Create('Error: No columns have been selected'); + + // The proc ID map is only needed to resolve the ProcName column + var LNeedProcNames := False; + for var I := 0 to High(LColumns) do + if (LColumns[I] = colkProcName) and LExportColumns[I] then LNeedProcNames := True; + var LProcIDMapFilename := TPath.Combine(FDataDir, ALCodeProfilerProcIDMapFilename); + If LNeedProcNames and (not TFile.Exists(LProcIDMapFilename)) then + raise Exception.CreateFmt('The required file "%s" does not exist. Please ensure it is available before proceeding', [ALCodeProfilerProcIDMapFilename]); + + // Ask where to save the CSV file + var LCsvFilename: String; + var LSaveDialog := TSaveDialog.Create(nil); + try + LSaveDialog.Title := 'Export to CSV'; + LSaveDialog.Filter := 'CSV files (*.csv)|*.csv|All files (*.*)|*.*'; + LSaveDialog.DefaultExt := 'csv'; + LSaveDialog.Options := LSaveDialog.Options + [ofOverwritePrompt]; + LSaveDialog.FileName := ALStringReplaceW(LProcMetricsFilenameOnly, '.dat', '.csv', [rfIgnoreCase]); + if not LSaveDialog.Execute then exit; + LCsvFilename := LSaveDialog.FileName; + finally + ALFreeAndNil(LSaveDialog); + end; + + ExportToCsvBtn.Cursor := crHourGlass; + var LExportedRecordCount: Int64 := 0; + Try + + // Load ALCodeProfilerProcIDMap.txt in LProcNames + var LProcNames := TDictionary<Cardinal, AnsiString>.Create; + var LProcMetricsStream: TFileStream := nil; + LCsvStream := nil; + try + if LNeedProcNames then begin + var LProcIDMap := TALHashedStringListA.Create; + try + LProcIDMap.LoadFromFile(LProcIDMapFilename); + for var I := 0 to LProcIDMap.Count - 1 do + LProcNames.AddOrSetValue(Cardinal(ALStrToInt(LProcIDMap.Names[I])), LProcIDMap.ValueFromIndex[I]); + finally + ALFreeAndNil(LProcIDMap); + end; + end; + + // Convert ALCodeProfilerProcMetrics.dat to CSV chunk by chunk as the + // file can be very huge and can not be fully loaded in memory + LProcMetricsStream := TFileStream.Create(LProcMetricsFilename, fmOpenRead or fmShareDenyWrite); + if LProcMetricsStream.Size mod LRawRecordSize <> 0 then + raise Exception.CreateFmt('The file "%s" is corrupted', [LProcMetricsFilenameOnly]); + var LTotalRecordCount: Int64 := LProcMetricsStream.Size div LRawRecordSize; + LCsvStream := TFileStream.Create(LCsvFilename, fmCreate); + var LRawBuffer: TBytes; + SetLength(LRawBuffer, 65536 * LRawRecordSize); // ~2 MB chunk + Setlength(LCsvBuffer, 4194304); // 4 MB + LCsvBufferPos := 0; + var LCsvHeader: AnsiString := ''; + var LFirstColumn := True; + for var I := 0 to High(LColumns) do + if LExportColumns[I] then + _AppendToCsvRow(LCsvHeader, LFirstColumn, GetProcMetricsColumnName(LColumns[I])); + _WriteToCsvBuffer(LCsvHeader + #13#10); + While True do begin + var LBytesRead := LProcMetricsStream.Read(LRawBuffer[0], Length(LRawBuffer)); + if LBytesRead <= 0 then break; + if LBytesRead mod LRawRecordSize <> 0 then + raise Exception.CreateFmt('The file "%s" is corrupted', [LProcMetricsFilenameOnly]); + for var I := 0 to (LBytesRead div LRawRecordSize) - 1 do begin + var LRec: TALProcMetrics; + DecodeProcMetricsRaw(LHistoryGroupMode, LRawBuffer, I * LRawRecordSize, LRec); + var LCsvRow: AnsiString := ''; + LFirstColumn := True; + for var J := 0 to High(LColumns) do + if LExportColumns[J] then + _AppendToCsvRow(LCsvRow, LFirstColumn, GetProcMetricsColumnValue(LColumns[J], LRec, LProcNames)); + _WriteToCsvBuffer(LCsvRow + #13#10); + inc(LExportedRecordCount); + end; + if LTotalRecordCount > 0 then begin + MainStatusBar.Panels[1].Text := 'Exporting to CSV: ' + ALIntToStrW(Round((LExportedRecordCount / LTotalRecordCount) * 100)) + '%'; + MainStatusBar.Update; + end; + end; + _FlushCsvBuffer; + finally + ALFreeAndNil(LProcNames); + ALFreeAndNil(LProcMetricsStream); + ALFreeAndNil(LCsvStream); + end; + + Finally + ExportToCsvBtn.Cursor := crDefault; + MainStatusBar.Panels[1].Text := ''; + End; + + MessageDlg(ALIntToStrW(LExportedRecordCount) + ' records have been exported successfully.', mtInformation, [mbOK], 0); +end; + {**********************************************} procedure TMainForm.FormCreate(Sender: TObject); begin FDataDir := ALGetModulePathW + 'data\'; + FLoadingSettings := True; FProcIDSequence := 0; Setlength(FProcMetrics, 0); FProcIDMap := TALHashedStringListA.Create; - FTreeListProcMetricsTailNode := nil; + ResetTreeListProcMetrics; FFilterProcIDs := THashSet<Cardinal>.Create; FFilterExecutionIDs := THashSet<Cardinal>.Create; FFilterParentExecutionIDs := THashSet<Cardinal>.Create; @@ -764,6 +1726,7 @@ procedure TMainForm.FormCreate(Sender: TObject); FFilterStartTimeStampMin := 0; FFilterStartTimeStampMax := 0; FOverrideFilterParentExecutionID := 0; + FOverrideFilterThreadID := High(Cardinal); FGoBackStack := TDictionary<Int64{ExecutionID}, TGoBackStackItem>.Create; FHttpServerCriticalSection := TCriticalSection.Create; //-- @@ -784,12 +1747,17 @@ procedure TMainForm.FormCreate(Sender: TObject); end; var LIniFile := TIniFile.Create(TPath.Combine(FDataDir, ConfigFilename)); try - HttpServerNameEdit.Text := LIniFile.ReadString('General','ServerName', ''); - HttpServerPortEdit.Text := LIniFile.ReadString('General','ServerPort', '8080'); SourcesPathMemo.Text := ALStringReplaceW(LIniFile.ReadString('General','SourcesPath', '..\..\Source\;..\..\Embarcadero\Florence\fmx\;..\..\Demos\ALFmxDynamicListBox\_Source\'), ';', #13#10, [RfReplaceALL]); + CodeProfilerIncFilenameEdit.Text := LIniFile.ReadString('General','CodeProfilerIncFilename', '..\..\Source\Alcinoe.CodeProfiler.inc'); finally ALFreeAndNil(LIniFile); end; + FLoadingSettings := False; + // Must be done after Config.ini has been read, as the location of + // Alcinoe.CodeProfiler.inc is one of its settings. + LoadCodeProfilerIncFile; + FHistoryGroupMode := GetSelectedHistoryGroupModeEnum; + UpdateHistoryGroupModeUI; InstrumentationTabSheetResize(nil); PanelfilterResize(nil); end; @@ -814,6 +1782,9 @@ procedure TMainForm.GridTableViewProcMetricsCellDblClick( AShift: TShiftState; var AHandled: Boolean); begin + // In hgmByProcID, calls are grouped by ProcID only, with no parent/child + // relationship recorded, so there is nothing to drill into. + if FHistoryGroupMode = hgmByProcID then Exit; if ACellViewInfo.GridRecord <> nil then begin var LGoBackStackItem: TGoBackStackItem; LGoBackStackItem.TopRowIndex := GridTableViewProcMetrics.Controller.TopRowIndex; @@ -832,12 +1803,15 @@ procedure TMainForm.GridTableViewProcMetricsCellDblClick( FGoBackStack.add(FOverrideFilterParentExecutionID, LGoBackStackItem); //-- FOverrideFilterParentExecutionID := ACellViewInfo.GridRecord.Values[GridTableViewProcMetricsColumnExecutionID.Index]; - if FTreeListProcMetricsTailNode = nil then FTreeListProcMetricsTailNode := TreeListProcMetrics.Add - else FTreeListProcMetricsTailNode := TreeListProcMetrics.AddChild(FTreeListProcMetricsTailNode); + FOverrideFilterThreadID := ACellViewInfo.GridRecord.Values[GridTableViewProcMetricsColumnThreadID.Index]; + FTreeListProcMetricsTailNode := TreeListProcMetrics.AddChild(FTreeListProcMetricsTailNode); FTreeListProcMetricsTailNode.Texts[TreeListProcMetricsColumnExecutionID.ItemIndex] := ACellViewInfo.GridRecord.Values[GridTableViewProcMetricsColumnExecutionID.Index]; FTreeListProcMetricsTailNode.Texts[TreeListProcMetricsColumnThreadID.ItemIndex] := ACellViewInfo.GridRecord.Values[GridTableViewProcMetricsColumnThreadID.Index]; FTreeListProcMetricsTailNode.Texts[TreeListProcMetricsColumnProcName.ItemIndex] := ACellViewInfo.GridRecord.Values[GridTableViewProcMetricsColumnProcName.Index]; - FTreeListProcMetricsTailNode.Texts[TreeListProcMetricsColumnStartTimeStamp.ItemIndex] := ACellViewInfo.GridRecord.Values[GridTableViewProcMetricsColumnStartTimeStamp.Index]; + if FHistoryGroupMode = hgmNone then + FTreeListProcMetricsTailNode.Texts[TreeListProcMetricsColumnStartTimeStamp.ItemIndex] := ACellViewInfo.GridRecord.Values[GridTableViewProcMetricsColumnStartTimestamp.Index] + else + FTreeListProcMetricsTailNode.Texts[TreeListProcMetricsColumnCallCount.ItemIndex] := ACellViewInfo.GridRecord.Values[GridTableViewProcMetricsColumnCallCount.Index]; FTreeListProcMetricsTailNode.Texts[TreeListProcMetricsColumnTimeTaken.ItemIndex] := ACellViewInfo.GridRecord.Values[GridTableViewProcMetricsColumnTimeTaken.Index]; TreeListProcMetrics.FullExpand; TreeListProcMetrics.TopVisibleNode := FTreeListProcMetricsTailNode; @@ -864,14 +1838,26 @@ procedure TMainForm.GridTableViewProcMetricsColumnStartTimestampGetDisplayText(S {**********************************************************************} procedure TMainForm.HttpServerPortEditPropertiesChange(Sender: TObject); begin + MainStatusBar.Panels[1].Text := ''; + SaveCodeProfilerIncFile; IdHTTPServer.Active := False; - if ALTrim(HttpServerPortEdit.Text) <> '' then begin + if GetSelectedServerName <> '' then begin IdHTTPServer.DefaultPort := ALStrToInt(ALTrim(HttpServerPortEdit.Text)); - IdHTTPServer.Active := True; - MainStatusBar.Panels[0].Text := 'Listening on port ' + HttpServerPortEdit.Text; + try + IdHTTPServer.Active := True; + MainStatusBar.Panels[0].Text := 'Listening on port ' + HttpServerPortEdit.Text; + except + MainStatusBar.Panels[0].Text := 'Not listening'; + Raise; + end; end else MainStatusBar.Panels[0].Text := 'Not listening'; - MainStatusBar.Panels[1].Text := ''; +end; + +{**********************************************************************} +procedure TMainForm.HttpServerNameEditPropertiesChange(Sender: TObject); +begin + SaveCodeProfilerIncFile; end; {**********************************************************************************************************************************************} @@ -897,14 +1883,17 @@ procedure TMainForm.TreeListProcMetricsDblClick(Sender: TObject); var LfocusedNode := TreeListProcMetrics.focusedNode; if Assigned(LClickedNode) and Assigned(LfocusedNode) then begin FOverrideFilterParentExecutionID := LfocusedNode.Values[TreeListProcMetricsColumnExecutionID.ItemIndex]; + if LfocusedNode = FTreeListProcMetricsRootNode then + FOverrideFilterThreadID := High(Cardinal) + else + FOverrideFilterThreadID := LfocusedNode.Values[TreeListProcMetricsColumnThreadID.ItemIndex]; FTreeListProcMetricsTailNode := LfocusedNode; FTreeListProcMetricsTailNode.DeleteChildren; end else begin - if FTreeListProcMetricsTailNode = nil then exit; FOverrideFilterParentExecutionID := 0; - FTreeListProcMetricsTailNode := nil; - TreeListProcMetrics.Clear; + FOverrideFilterThreadID := High(Cardinal); + ResetTreeListProcMetrics; end; var LGoBackStackItem: TGoBackStackItem; @@ -975,8 +1964,11 @@ procedure TMainForm.ApplyFilterBtnClick(Sender: TObject); FFilterProcIDs.Add(ALMaxUInt); end; - if (FFilterStartTimeStampMin > 0) or - (FFilterStartTimeStampMax > 0) then begin + // The start-timestamp filter only makes sense for individual calls, + // which only exist in hgmNone; grouped modes have no StartTimeStamp. + if (FHistoryGroupMode = hgmNone) and + ((FFilterStartTimeStampMin > 0) or + (FFilterStartTimeStampMax > 0)) then begin var LExecutionDict := TDictionary<Cardinal, Cardinal>.create; Try for var I := low(FProcMetrics) to high(FProcMetrics) do begin @@ -1012,8 +2004,7 @@ procedure TMainForm.ApplyFilterBtnClick(Sender: TObject); (FFilterExecutionIDs.Count = 0) then FFilterParentExecutionIDs.Add(0); - FTreeListProcMetricsTailNode := nil; - TreeListProcMetrics.Clear; + ResetTreeListProcMetrics; FGoBackStack.Clear; Refresh;