unit pascodegen; {$mode objfpc}{$H+} interface uses Classes, SysUtils; Type TCodegenLogType = (cltInfo); TCodegenLogTypes = Set of TCodegenLogType; TCodeGeneratorLogEvent = Procedure (Sender : TObject; LogType : TCodegenLogType; Const Msg : String) of object; TCodesection = (csUnknown, csConst, csType, csVar, csResourcestring, csDeclaration); { TPascalCodeGenerator } TPascalCodeGenerator = Class(TComponent) Private FAddTimeStamp: Boolean; FExtraUnits: String; FKeywordPrefix: String; FKeywordSuffix: String; FLicenseText: TStrings; FOnLog: TCodeGeneratorLogEvent; FOutputUnitName: String; FSource : TStrings; Findent : String; FSections : Array of TCodeSection; FSectionCount : Integer; FSwitches: TStrings; function GetSection: TCodeSection; procedure SetLicenseText(AValue: TStrings); procedure SetSection(AValue: TCodeSection); procedure SetSwitches(AValue: TStrings); Protected // Source manipulation Procedure DoLog(Const Msg : String; AType : TCodegenLogType = cltInfo); Procedure DoLog(Const Fmt : String; Args : Array of const; AType : TCodegenLogType = cltInfo); Function BaseUnits : String; virtual; Public Public Constructor Create(AOwner : TComponent); override; Destructor Destroy; override; // Emit section type word Procedure EnsureSection(aSection : TCodeSection); Procedure PushSection(ASection : TCodeSection = csUnknown); Function PopSection : TCodeSection; Procedure CreateHeader; virtual; Procedure CreateUnitClause; virtual; Procedure Indent; Procedure Undent; Function IsKeyWord (Const S : String) : Boolean; Function EscapeKeyWord(Const S : String) : String; Function MakePascalString(S: String; AddQuotes: Boolean=False): String; Function PrettyPrint(Const S: string): String; Procedure AddLn(Const Aline: string); Procedure AddLn(Const Alines : array of string); Procedure AddLn(Const Alines : TStrings); Procedure AddLn(Const Fmt: string; Args : Array of const); Procedure Comment(Const AComment : String; Curly : Boolean = False); Procedure Comment(Const AComment : Array of String); Procedure Comment(Const AComment : TStrings); Procedure ClassHeader(Const AClassName: String); virtual; Procedure SimpleMethodBody(Lines: Array of string); virtual; procedure SaveToStream(const AStream: TStream); Procedure SaveToFile(Const AFileName : string); Property Source : TStrings Read FSource; Property CurrentSection : TCodeSection Read GetSection Write SetSection; Published Property OutputUnitName : String Read FOutputUnitName Write FOutputUnitName; Property ExtraUnits : String Read FExtraUnits Write FExtraUnits; Property LicenseText : TStrings Read FLicenseText Write SetLicenseText; Property Switches : TStrings Read FSwitches Write SetSwitches; Property OnLog : TCodeGeneratorLogEvent Read FOnLog Write FOnlog; Property AddTimeStamp : Boolean Read FAddTimeStamp Write FAddTimeStamp; Property KeywordSuffix : String Read FKeywordSuffix Write FKeywordSuffix; Property KeywordPrefix : String Read FKeywordPrefix Write FKeywordPrefix; end; implementation { TPascalCodeGenerator } procedure TPascalCodeGenerator.Indent; begin FIndent:=FIndent+StringOfChar(' ',2); end; procedure TPascalCodeGenerator.Undent; Var L : Integer; begin L:=Length(Findent); if L>0 then FIndent:=Copy(FIndent,1,L-2) end; function TPascalCodeGenerator.IsKeyWord(const S: String): Boolean; Const KW=';absolute;and;array;asm;begin;case;const;constructor;destructor;div;do;'+ 'downto;else;end;file;for;function;goto;if;implementation;in;inherited;'+ 'inline;interface;label;mod;nil;not;object;of;on;operator;or;packed;'+ 'procedure;program;record;reintroduce;repeat;self;set;shl;shr;string;then;'+ 'to;type;unit;until;uses;var;while;with;xor;dispose;exit;false;new;true;'+ 'as;class;dispinterface;except;exports;finalization;finally;initialization;'+ 'inline;is;library;on;out;packed;property;raise;resourcestring;threadvar;try;'+ 'private;published;length;setlength;'; begin Result:=Pos(';'+lowercase(S)+';',KW)<>0; end; function TPascalCodeGenerator.EscapeKeyWord(const S: String): String; begin Result:=S; if IsKeyWord(S) then Result:=KeywordPrefix+Result+KeywordSuffix end; procedure TPascalCodeGenerator.AddLn(const Aline: string); begin FSource.Add(FIndent+ALine); end; procedure TPascalCodeGenerator.AddLn(const Alines: array of string); Var S : String; begin For s in alines do Addln(S); end; procedure TPascalCodeGenerator.AddLn(const Alines: TStrings); Var S : String; begin For s in alines do Addln(S); end; procedure TPascalCodeGenerator.AddLn(const Fmt: string; Args: array of const); begin AddLn(Format(Fmt,Args)); end; procedure TPascalCodeGenerator.Comment(const AComment: String; Curly: Boolean); begin if Curly then AddLn('{ '+AComment+' }') else AddLn('// '+AComment); end; procedure TPascalCodeGenerator.Comment(const AComment: array of String); begin AddLn('{'); Indent; AddLn(AComment); Undent; AddLn('}'); end; procedure TPascalCodeGenerator.Comment(const AComment: TStrings); begin AddLn('{'); Indent; AddLn(AComment); Undent; AddLn('}'); end; constructor TPascalCodeGenerator.Create(AOwner: TComponent); begin inherited Create(AOwner); FSource:=TstringList.Create; FLicenseText:=TstringList.Create; FSwitches:=TstringList.Create; FSwitches.Add('MODE ObjFPC'); FSwitches.Add('H+'); SetLength(FSections,0); FSectionCount:=0; PushSection(csUnknown); FKeywordPrefix:='&'; end; destructor TPascalCodeGenerator.Destroy; begin FreeAndNil(FSwitches); FreeAndNil(FLicenseText); FreeAndNil(FSource); inherited Destroy; end; procedure TPascalCodeGenerator.EnsureSection(aSection: TCodeSection); Const SectionKeyWords : Array[TCodesection] of string = ('', 'Const', 'Type', 'Var', 'Resourcestring', ''); begin If CurrentSection<>aSection then begin CurrentSection:=aSection; AddLn(SectionKeyWords[CurrentSection]); end; end; procedure TPascalCodeGenerator.PushSection(ASection : TCodeSection = csUnknown); begin if FSectionCount=Length(FSections) then SetLength(FSections,FSectionCount+10); FSections[FSectionCount]:=ASection; Inc(FSectionCount); end; function TPascalCodeGenerator.PopSection: TCodeSection; begin if FSectionCount=0 then Result:=csUnknown else begin Dec(FSectionCount); Result:=FSections[FSectionCount]; end; end; procedure TPascalCodeGenerator.SaveToStream(const AStream : TStream); begin FSource.SaveToStream(AStream) end; procedure TPascalCodeGenerator.SaveToFile(const AFileName: string); Var F : TFileStream; B : Boolean; begin B:=False; F:=Nil; try B:=(Source.Count=0) and (OutputUnitName=''); if B then OutputUnitname:=ChangeFileExt(ExtractFileName(AFileName),''); F:=TFileStream.Create(aFilename,fmCreate); SaveToStream(F); finally F.Free; if B then OutputUnitName:=''; end; end; procedure TPascalCodeGenerator.SetSection(AValue: TCodeSection); begin if GetSection=AValue then Exit; FSections[FSectionCount-1]:=AValue; end; procedure TPascalCodeGenerator.SetSwitches(AValue: TStrings); begin if FSwitches=AValue then Exit; FSwitches.Assign(AValue); end; function TPascalCodeGenerator.GetSection: TCodeSection; begin Result:=FSections[FSectionCount-1]; end; procedure TPascalCodeGenerator.SetLicenseText(AValue: TStrings); begin if FLicenseText=AValue then Exit; FLicenseText.Assign(AValue); end; procedure TPascalCodeGenerator.DoLog(const Msg: String; AType: TCodegenLogType); begin If Assigned(FOnLog) then FOnLog(Self,Atype,Msg); end; procedure TPascalCodeGenerator.DoLog(const Fmt: String; Args: array of const; AType: TCodegenLogType); begin DoLog(Format(Fmt,Args),AType); end; procedure TPascalCodeGenerator.CreateHeader; Var B,S : String; begin if LicenseText.Count>0 then Comment(LicenseText); if AddTimeStamp then Comment('Generated on: '+DateTimeToStr(Now)); For S in Switches do addln('{$%s}',[S]); addln(''); addln('interface'); addln(''); S:=ExtraUnits; B:=BaseUnits; if (B<>'') then if (S<>'') then begin if (B[Length(B)]<>',') then B:=B+','; S:=B+S; end else S:=B; addln('uses %s;',[S]); addln(''); end; procedure TPascalCodeGenerator.CreateUnitClause; begin AddLn('Unit %s;',[OutputUnitName]); AddLn(''); end; procedure TPascalCodeGenerator.SimpleMethodBody(Lines: array of string); Var S : String; begin AddLn(''); AddLn('begin'); Indent; For S in Lines do AddLn(S); Undent; AddLn('end;'); AddLn(''); end; function TPascalCodeGenerator.BaseUnits: String; begin Result:=''; end; function TPascalCodeGenerator.MakePascalString(S: String; AddQuotes: Boolean ): String; begin Result:=StringReplace(S,'''','''''',[rfReplaceAll]); if AddQuotes then Result:=''''+Result+''''; end; function TPascalCodeGenerator.PrettyPrint(const S: string): String; begin If (S='') then Result:='' else Result:=Upcase(S[1])+Copy(S,2,Length(S)-1); end; procedure TPascalCodeGenerator.ClassHeader(const AClassName: String); begin AddLn(''); AddLn('{ '+StringOfChar('-',68)); AddLn(' '+AClassName); AddLn(' '+StringOfChar('-',68)+'}'); AddLn(''); end; end. end.