pas2js/compiler/packages/compat/pascodegen.pp
2018-06-23 14:10:58 +00:00

389 lines
9.3 KiB
ObjectPascal

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.