fpc/fcl/passrc/paswrite.pp

621 lines
15 KiB
ObjectPascal

{
This file is part of the Free Component Library
Pascal tree source file writer
Copyright (c) 2003 by
Areca Systems GmbH / Sebastian Guenther, sg@freepascal.org
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 PasWrite;
interface
uses Classes, PasTree;
type
TPasWriter = class
private
FStream: TStream;
IsStartOfLine: Boolean;
Indent, CurDeclSection: string;
DeclSectionStack: TList;
procedure IncIndent;
procedure DecIndent;
procedure IncDeclSectionLevel;
procedure DecDeclSectionLevel;
procedure PrepareDeclSection(const ADeclSection: string);
public
constructor Create(AStream: TStream);
destructor Destroy; override;
procedure wrt(const s: string);
procedure wrtln(const s: string);
procedure wrtln;
procedure WriteElement(AElement: TPasElement);
procedure WriteType(AType: TPasType);
procedure WriteModule(AModule: TPasModule);
procedure WriteSection(ASection: TPasSection);
procedure WriteClass(AClass: TPasClassType);
procedure WriteVariable(AVar: TPasVariable);
procedure WriteProcDecl(AProc: TPasProcedure);
procedure WriteProcImpl(AProc: TPasProcedureImpl);
procedure WriteProperty(AProp: TPasProperty);
procedure WriteImplBlock(ABlock: TPasImplBlock);
procedure WriteImplElement(AElement: TPasImplElement;
AAutoInsertBeginEnd: Boolean);
procedure WriteImplCommand(ACommand: TPasImplCommand);
procedure WriteImplCommands(ACommands: TPasImplCommands);
procedure WriteImplIfElse(AIfElse: TPasImplIfElse);
procedure WriteImplForLoop(AForLoop: TPasImplForLoop);
property Stream: TStream read FStream;
end;
procedure WritePasFile(AElement: TPasElement; const AFilename: string);
procedure WritePasFile(AElement: TPasElement; AStream: TStream);
implementation
uses SysUtils;
type
PDeclSectionStackElement = ^TDeclSectionStackElement;
TDeclSectionStackElement = record
LastDeclSection, LastIndent: string;
end;
constructor TPasWriter.Create(AStream: TStream);
begin
FStream := AStream;
IsStartOfLine := True;
DeclSectionStack := TList.Create;
end;
destructor TPasWriter.Destroy;
var
i: Integer;
El: PDeclSectionStackElement;
begin
for i := 0 to DeclSectionStack.Count - 1 do
begin
El := PDeclSectionStackElement(DeclSectionStack[i]);
Dispose(El);
end;
DeclSectionStack.Free;
inherited Destroy;
end;
procedure TPasWriter.wrt(const s: string);
begin
if IsStartOfLine then
begin
if Length(Indent) > 0 then
Stream.Write(Indent[1], Length(Indent));
IsStartOfLine := False;
end;
Stream.Write(s[1], Length(s));
end;
const
LF: string = #10;
procedure TPasWriter.wrtln(const s: string);
begin
wrt(s);
Stream.Write(LF[1], 1);
IsStartOfLine := True;
end;
procedure TPasWriter.wrtln;
begin
Stream.Write(LF[1], 1);
IsStartOfLine := True;
end;
procedure TPasWriter.WriteElement(AElement: TPasElement);
begin
if AElement.ClassType = TPasModule then
WriteModule(TPasModule(AElement))
else if AElement.ClassType = TPasSection then
WriteSection(TPasSection(AElement))
else if AElement.ClassType = TPasVariable then
WriteVariable(TPasVariable(AElement))
else if AElement.InheritsFrom(TPasType) then
WriteType(TPasType(AElement))
else if AElement.InheritsFrom(TPasProcedure) then
WriteProcDecl(TPasProcedure(AElement))
else if AElement.InheritsFrom(TPasProcedureImpl) then
WriteProcImpl(TPasProcedureImpl(AElement))
else if AElement.ClassType = TPasProperty then
WriteProperty(TPasProperty(AElement))
else
raise Exception.Create('Writing not implemented for ' +
AElement.ElementTypeName + ' nodes');
end;
procedure TPasWriter.WriteType(AType: TPasType);
begin
if AType.ClassType = TPasUnresolvedTypeRef then
wrt(AType.Name)
else if AType.ClassType = TPasClassType then
WriteClass(TPasClassType(AType))
else
raise Exception.Create('Writing not implemented for ' +
AType.ElementTypeName + ' nodes');
end;
procedure TPasWriter.WriteModule(AModule: TPasModule);
begin
wrtln('unit ' + AModule.Name + ';');
wrtln;
wrtln('interface');
wrtln;
WriteSection(AModule.InterfaceSection);
Indent := '';
wrtln;
wrtln;
wrtln('implementation');
if Assigned(AModule.ImplementationSection) then
begin
wrtln;
WriteSection(AModule.ImplementationSection);
end;
wrtln;
wrtln('end.');
end;
procedure TPasWriter.WriteSection(ASection: TPasSection);
var
i: Integer;
begin
if ASection.UsesList.Count > 0 then
begin
wrt('uses ');
for i := 0 to ASection.UsesList.Count - 1 do
begin
if i > 0 then
wrt(', ');
wrt(TPasElement(ASection.UsesList[i]).Name);
end;
wrtln(';');
wrtln;
end;
CurDeclSection := '';
for i := 0 to ASection.Declarations.Count - 1 do
WriteElement(TPasElement(ASection.Declarations[i]));
end;
procedure TPasWriter.WriteClass(AClass: TPasClassType);
var
i: Integer;
Member: TPasElement;
LastVisibility, CurVisibility: TPasMemberVisibility;
begin
PrepareDeclSection('type');
wrt(AClass.Name + ' = ');
if AClass.IsPacked then
wrt('packed '); // 12/04/04 - Dave - Added
case AClass.ObjKind of
okObject: wrt('object');
okClass: wrt('class');
okInterface: wrt('interface');
end;
if Assigned(AClass.AncestorType) then
wrtln('(' + AClass.AncestorType.Name + ')')
else
wrtln;
IncIndent;
LastVisibility := visDefault;
for i := 0 to AClass.Members.Count - 1 do
begin
Member := TPasElement(AClass.Members[i]);
CurVisibility := Member.Visibility;
if CurVisibility <> LastVisibility then
begin
DecIndent;
case CurVisibility of
visPrivate: wrtln('private');
visProtected: wrtln('protected');
visPublic: wrtln('public');
visPublished: wrtln('published');
visAutomated: wrtln('automated');
end;
IncIndent;
LastVisibility := CurVisibility;
end;
WriteElement(Member);
end;
DecIndent;
wrtln('end;');
wrtln;
end;
procedure TPasWriter.WriteVariable(AVar: TPasVariable);
begin
if (AVar.Parent.ClassType <> TPasClassType) and
(AVar.Parent.ClassType <> TPasRecordType) then
PrepareDeclSection('var');
wrt(AVar.Name + ': ');
WriteType(AVar.VarType);
wrtln(';');
end;
procedure TPasWriter.WriteProcDecl(AProc: TPasProcedure);
var
i: Integer;
begin
wrt(AProc.TypeName + ' ' + AProc.Name);
if Assigned(AProc.ProcType) and (AProc.ProcType.Args.Count > 0) then
begin
wrt('(');
for i := 0 to AProc.ProcType.Args.Count - 1 do
with TPasArgument(AProc.ProcType.Args[i]) do
begin
if i > 0 then
wrt('; ');
case Access of
argConst: wrt('const ');
argVar: wrt('var ');
end;
wrt(Name);
if Assigned(ArgType) then
begin
wrt(': ');
WriteElement(ArgType);
end;
if Value <> '' then
wrt(' = ' + Value);
end;
wrt(')');
end;
if Assigned(AProc.ProcType) and
(AProc.ProcType.ClassType = TPasFunctionType) then
begin
wrt(': ');
WriteElement(TPasFunctionType(AProc.ProcType).ResultEl.ResultType);
end;
wrt(';');
if AProc.IsVirtual then
wrt(' virtual;');
if AProc.IsDynamic then
wrt(' dynamic;');
if AProc.IsAbstract then
wrt(' abstract;');
if AProc.IsOverride then
wrt(' override;');
if AProc.IsOverload then
wrt(' overload;');
if AProc.IsReintroduced then
wrt(' reintroduce;');
if AProc.IsStatic then
wrt(' static;');
// !!!: Not handled: Message, calling conventions
wrtln;
end;
procedure TPasWriter.WriteProcImpl(AProc: TPasProcedureImpl);
var
i: Integer;
begin
PrepareDeclSection('');
wrt(AProc.TypeName + ' ');
if AProc.Parent.ClassType = TPasClassType then
wrt(AProc.Parent.Name + '.');
wrt(AProc.Name);
if Assigned(AProc.ProcType) and (AProc.ProcType.Args.Count > 0) then
begin
wrt('(');
for i := 0 to AProc.ProcType.Args.Count - 1 do
with TPasArgument(AProc.ProcType.Args[i]) do
begin
if i > 0 then
wrt('; ');
case Access of
argConst: wrt('const ');
argVar: wrt('var ');
end;
wrt(Name);
if Assigned(ArgType) then
begin
wrt(': ');
WriteElement(ArgType);
end;
if Value <> '' then
wrt(' = ' + Value);
end;
wrt(')');
end;
if Assigned(AProc.ProcType) and
(AProc.ProcType.ClassType = TPasFunctionType) then
begin
wrt(': ');
WriteElement(TPasFunctionType(AProc.ProcType).ResultEl.ResultType);
end;
wrtln(';');
IncDeclSectionLevel;
for i := 0 to AProc.Locals.Count - 1 do
begin
if TPasElement(AProc.Locals[i]).InheritsFrom(TPasProcedureImpl) then
begin
IncIndent;
if (i = 0) or not
TPasElement(AProc.Locals[i - 1]).InheritsFrom(TPasProcedureImpl) then
wrtln;
end;
WriteElement(TPasElement(AProc.Locals[i]));
if TPasElement(AProc.Locals[i]).InheritsFrom(TPasProcedureImpl) then
DecIndent;
end;
DecDeclSectionLevel;
wrtln('begin');
IncIndent;
if Assigned(AProc.Body) then
WriteImplBlock(AProc.Body);
DecIndent;
wrtln('end;');
wrtln;
end;
procedure TPasWriter.WriteProperty(AProp: TPasProperty);
var
i: Integer;
begin
wrt('property ' + AProp.Name);
if AProp.Args.Count > 0 then
begin
wrt('[');
for i := 0 to AProp.Args.Count - 1 do;
// !!!: Create WriteArgument method and call it here
wrt(']');
end;
if Assigned(AProp.VarType) then
begin
wrt(': ');
WriteType(AProp.VarType);
end;
if AProp.ReadAccessorName <> '' then
wrt(' read ' + AProp.ReadAccessorName);
if AProp.WriteAccessorName <> '' then
wrt(' write ' + AProp.WriteAccessorName);
if AProp.StoredAccessorName <> '' then
wrt(' stored ' + AProp.StoredAccessorName);
if AProp.DefaultValue <> '' then
wrt(' default ' + AProp.DefaultValue);
if AProp.IsNodefault then
wrt(' nodefault');
if AProp.IsDefault then
wrt('; default');
wrtln(';');
end;
procedure TPasWriter.WriteImplBlock(ABlock: TPasImplBlock);
var
i: Integer;
begin
for i := 0 to ABlock.Elements.Count - 1 do
begin
WriteImplElement(TPasImplElement(ABlock.Elements[i]), False);
if TPasImplElement(ABlock.Elements[i]).ClassType = TPasImplCommand then
wrtln(';');
end;
end;
procedure TPasWriter.WriteImplElement(AElement: TPasImplElement;
AAutoInsertBeginEnd: Boolean);
begin
if AElement.ClassType = TPasImplCommand then
WriteImplCommand(TPasImplCommand(AElement))
else if AElement.ClassType = TPasImplCommands then
begin
DecIndent;
if AAutoInsertBeginEnd then
wrtln('begin');
IncIndent;
WriteImplCommands(TPasImplCommands(AElement));
DecIndent;
if AAutoInsertBeginEnd then
wrtln('end;');
IncIndent;
end else if AElement.ClassType = TPasImplBlock then
begin
DecIndent;
if AAutoInsertBeginEnd then
wrtln('begin');
IncIndent;
WriteImplBlock(TPasImplBlock(AElement));
DecIndent;
if AAutoInsertBeginEnd then
wrtln('end;');
IncIndent;
end else if AElement.ClassType = TPasImplIfElse then
WriteImplIfElse(TPasImplIfElse(AElement))
else if AElement.ClassType = TPasImplForLoop then
WriteImplForLoop(TPasImplForLoop(AElement))
else
raise Exception.Create('Writing not yet implemented for ' +
AElement.ClassName + ' implementation elements');
end;
procedure TPasWriter.WriteImplCommand(ACommand: TPasImplCommand);
begin
wrt(ACommand.Command);
end;
procedure TPasWriter.WriteImplCommands(ACommands: TPasImplCommands);
var
i: Integer;
s: string;
begin
for i := 0 to ACommands.Commands.Count - 1 do
begin
s := ACommands.Commands[i];
if Length(s) > 0 then
if (Length(s) >= 2) and (s[1] = '/') and (s[2] = '/') then
wrtln(s)
else
wrtln(s + ';');
end;
end;
procedure TPasWriter.WriteImplIfElse(AIfElse: TPasImplIfElse);
begin
wrt('if ' + AIfElse.Condition + ' then');
if Assigned(AIfElse.IfBranch) then
begin
wrtln;
if (AIfElse.IfBranch.ClassType = TPasImplCommands) or
(AIfElse.IfBranch.ClassType = TPasImplBlock) then
wrtln('begin');
IncIndent;
WriteImplElement(AIfElse.IfBranch, False);
DecIndent;
if (AIfElse.IfBranch.ClassType = TPasImplCommands) or
(AIfElse.IfBranch.ClassType = TPasImplBlock) then
if Assigned(AIfElse.ElseBranch) then
wrt('end ')
else
wrtln('end;')
else
if Assigned(AIfElse.ElseBranch) then
wrtln;
end else
if not Assigned(AIfElse.ElseBranch) then
wrtln(';')
else
wrtln;
if Assigned(AIfElse.ElseBranch) then
if AIfElse.ElseBranch.ClassType = TPasImplIfElse then
begin
wrt('else ');
WriteImplElement(AIfElse.ElseBranch, True);
end else
begin
wrtln('else');
IncIndent;
WriteImplElement(AIfElse.ElseBranch, True);
if (not Assigned(AIfElse.Parent)) or
(AIfElse.Parent.ClassType <> TPasImplIfElse) or
(TPasImplIfElse(AIfElse.Parent).IfBranch <> AIfElse) then
wrtln(';');
DecIndent;
end;
end;
procedure TPasWriter.WriteImplForLoop(AForLoop: TPasImplForLoop);
begin
wrtln('for ' + AForLoop.Variable.Name + ' := ' + AForLoop.StartValue +
' to ' + AForLoop.EndValue + ' do');
IncIndent;
WriteImplElement(AForLoop.Body, True);
DecIndent;
if (AForLoop.Body.ClassType <> TPasImplBlock) and
(AForLoop.Body.ClassType <> TPasImplCommands) then
wrtln(';');
end;
procedure TPasWriter.IncIndent;
begin
Indent := Indent + ' ';
end;
procedure TPasWriter.DecIndent;
begin
if Indent = '' then
raise Exception.Create('Internal indent error');
SetLength(Indent, Length(Indent) - 2);
end;
procedure TPasWriter.IncDeclSectionLevel;
var
El: PDeclSectionStackElement;
begin
New(El);
DeclSectionStack.Add(El);
El^.LastDeclSection := CurDeclSection;
El^.LastIndent := Indent;
CurDeclSection := '';
end;
procedure TPasWriter.DecDeclSectionLevel;
var
El: PDeclSectionStackElement;
begin
El := PDeclSectionStackElement(DeclSectionStack[DeclSectionStack.Count - 1]);
DeclSectionStack.Delete(DeclSectionStack.Count - 1);
CurDeclSection := El^.LastDeclSection;
Indent := El^.LastIndent;
Dispose(El);
end;
procedure TPasWriter.PrepareDeclSection(const ADeclSection: string);
begin
if ADeclSection <> CurDeclSection then
begin
if CurDeclsection <> '' then
DecIndent;
if ADeclSection <> '' then
begin
wrtln(ADeclSection);
IncIndent;
end;
CurDeclSection := ADeclSection;
end;
end;
procedure WritePasFile(AElement: TPasElement; const AFilename: string);
var
Stream: TFileStream;
begin
Stream := TFileStream.Create(AFilename, fmCreate);
try
WritePasFile(AElement, Stream);
finally
Stream.Free;
end;
end;
procedure WritePasFile(AElement: TPasElement; AStream: TStream);
var
Writer: TPasWriter;
begin
Writer := TPasWriter.Create(AStream);
try
Writer.WriteElement(AElement);
finally
Writer.Free;
end;
end;
end.