Mit dem Projekt DelphiPrettyPrint.ttp kann die Lesbarkeit von Delphi-Quellcode verbessert werden, indem er auf einheitliche Weise so formatiert wird, dass die Programmstruktur hervortritt. Z.B der Code:
procedure TTable . InitFieldDefs;
begin if(FHandle<>nil) then InternalInitFieldDefs else begin SetDBFlag(dbfFieldList,True); end; end;
wird zu:
procedure TTable.InitFieldDefs; begin if ( FHandle <> nil ) then InternalInitFieldDefs else begin SetDBFlag ( dbfFieldList, True ); end; end;
Der Codeformatierer basiert auf dem Delphi-Parser Delphi.ttp und ist zunächst als Demonstration dafür gedacht, wie aus dem Parser erzeugte Parse-Bäume im TextTransformers zur Erzeugung eines erwünschten Ausgabeformats verwendet werden können. Der Formatierer kann aber durchaus auch als eigenständiges Projekt von Nutzen sein.
Das Projekt wurde inspiriert durch den Pascal-Prettyprinter:
Einige Merkmale dieses Ursprungs sind noch im gegenwärtigen Projekt zu erkennen. Für den Delphi-Prettyprinter reicht aber der token-basierte Ansatz des Pascal-Formatierers nicht aus. Im weit komplexeren Delphi ist es wichtig zu wissen, an welcher Stelle der Grammatik ein jeweiliges Token steht.
Die mit grundlegenden Aktionen sind die Einrückung und die Rücknahme der Einrückung des Rands. Jedes Mal, wenn der Seitenrand eingerückt wird, wird der vorherige Wert des Rands auf einen Stapel gelegt. Jedes Mal, wenn der Rand zurückgesetzt wird, wird der letzte Wert vom Stapel genommen, um den vorherigen Wert des Seitenrands zu erhalten.
Diese und andere Aktionen werden ausgelöst, wenn der Parse-Baum von der Spitze nach unten bis zum letzten Blatt durchlaufen wird. Die Aktionen sind in der Funktionstabelle "m_ftTree" den Labels der Knoten zugeordnet. Die Einrückungen werden dabei zumeist von Knoten ausgelöst, die Token repräsentieren, während die Zurücknahme der Einrückung meist am Ende bestimmter Produktionen erfolgt.
Ebenso wie im Pascal-Prettyprinter gibt es in DelphiPrettyPrint.ttp eine Tabelle, in der einzelnen Token ein Set von Optionen zugeordnet, die bestimmen, was bei der Ausgabe des Tokens geschen soll.
Die Optionen werden in der folgenden Reihenfolge verarbeitet und rufen die folgenden Aktionen auf:
CRSUPPRESS | - wenn nach dem vorherigen Symbol ein Flag für einen folgenden Zeilenumbruch gesetzt worden ist, wird es zurückgesetzt. |
CRBEFORE | - ein Zeilenumbruch wird vor dem aktuellen Symbol eingefügt, wenn noch nicht vorhanden |
BLANKLINEBEFORE | - eine Leerzeile wird vor dem aktuellen Symbol eingefügt, wenn noch nicht vorhanden |
DINDENT | - die Zeilenrandwerte werden vom Stapel genommen und der Rand wird entsrechend zurückgesetzt |
SPACEBEFORESUPPRESS | - wenn nach dem vorherigen Symbol ein Flag für einen folgenden Leerzeichen gesetzt worden ist, wird es zurückgesetzt. |
SPACEBEFORE | - eine Leerzeichen wird vor dem aktuellen Symbol ausgegeben, wenn noch nicht vorhanden. |
SPACEAFTERSUPPRESS | - ein auf das Symbol folgendes Leerzeichen wird unterdrückt |
INDENTBYTAB | - der Seitenrand wird ausgehend vom vorherigen Rand um einen Standardbetrag eingerückt |
INDENTTOCLP | - der Seitenrand wird bis zur aktuellen Zeilenposition eingerückt |
CRAFTER | - das Flag für eine auf das Symbol folgendes Leerzeile wird gesetzt. |
Die Optionen werden in der Initialisierungsroutine "InitPrettyPrint" als Attribute von Knoten gesetzt und in der map "m_mPrettyPrint" gespeichert. In der Aktion "Tree_keyword" werden sie als Kontext zu den folgenden Aktionen weitergeleitet.
Die Rücknahme einer Einrückung lässt sich meist nicht so leicht am Auftreten eine bestimmten Tokens festmachen, wie beim "compound_statement", das mit "begin" beginnt und mit "end" endet. Deshalb wird die PopIndent-Aktion meist am Ende bestimmter Produktionen ausgelöst. Z.B. ist dem "for_stmt", in der das Token "do" die Einrückung auslöst, die Aktion "Tree_deindent" zugeordnet.
m_ftTree.add("for_stmt", Tree_deindent);
Im generierten C++-Code sieht die Funktion zu aus:
void Csource_moduleParser::Tree_deindent(state_type& xState, const node& xnNode, str& xsResult, node& xnContext) { node n(Tree_Default( xState, xnNode, xsResult, xnContext )); PopIndent(); }
D.h. es wird zunächst die Defaultaktion "Tree_Default" ausgeführt, in der u.a. die dem "do" zugeordnete Aktion ausgelöst wird, und anschließend wird die PopIndent-Aktion ausgeführt, die den Rand zurücksetzt.
Kommentare werden beim Parsen in der map "m_Comments" als kleine Bäume gespeichert und im letzten Baumknoten als Attribut gemerkt. Bei der Abarbeitung des Delphi-Baums werden die Kommentare im wesentlichen unverändert ausgegeben. Dazu gibt es eine gesonderte Funktionstabelle "m_ftCommentTree" zur Behandlung der Kommentar-Knoten. Bei Zeilenkommentaren wird der Zeilenumbruch auf den sie enden nicht mitgespeichert. Um eventuelle Leerzeilen vermieden wird stattdessen wie bei der CRAFTER-Option ein Flag gesetzt, dass den Umbruch vor Ausgabe des nächsten Tokens bewirkt.
Am Anfang der Startregel "source_module" werden Zeichen und Grad der Einrückung gesetzt und können vom Benutzer leicht geändert werden.
{{ SetIndenter(' '); m_iIndent = 2; }}
Letztes Update: 31.12.09
1.1.6 lokale Einstellungen der Kommentar-Produktionen so korrigiert, dass keine Zeichen ignoriert werden
1.1.5
1.0.8 enthält viele der Änderungen von Delphi.ttp 1.1.0
to the top |