Start |
Text2HTML |
Wikipedia |
Yacc2TT |
Delphi-Parser |
Java-Parser |
C-Präprozessor |
C-Parser |
HTML4 |
Nützliches |
MIME-Parser |
Spamfilter |
Weitere Beispiele |
Freie Komponenten |
Mit c_pp.ttp ist ein TextTransformer-Projekt benannt, das einen C-Präprozessor nachbildet. Mit c_pp lassen sich C++-Dateien in die vorverarbeitete Form bringen, wie der Compiler sie "sieht": Präprozessor-Direktiven sind darin entfernt: Include-Dateien werden eingeschlossen, Definitionen werden ersetzt, nicht definierte Bereiche werden ausgelassen und Makros werden aufgelöst. Im Unterschied zu den existierenden Präprozessoren, die der Kompilierung von C++-Code vorausgeschaltet sind, wird mit c_pp nicht nur intermediär die vorverarbeitete Tokenfolge erzeugt, sondern ein vollständiger Text.
Der Name "c_pp" steht für C-Präprozessor. Durch den Unterstrich wird der Name von einem ebenfalls existierenden Cplusplus-Parser mit dem Namen "Cpp" unterschieden.
Die ursprüngliche Version dieses C-Präprozessors wurde entwickelt, um die Übersetzung einer in C++ verfassten Firmensoftware nach Java vorzubereiten. Es war also nicht das Ziel einen allgemeingültigen Präprozessor zu erzeugen, der mit allen Tricks einer Präprozessor Meta-Programmierung zurechtkommt. Die Zielsetzung war vielmehr pragmatisch: Aus der endlichen Anzahl von Dateien sollten die Präprozessor-Direktiven in einer Weise ersetzt werden, die den Sinn dieser Direktiven bewahrte.
|
Diese, auf die betreffende Firmensoftware zugeschnittenen Spezialbehandlungen wurden aus dem hier veröffentlichten c_pp-Projekt entfernt. Es ist aber leicht möglich, entsprechende Spezialbehandlungen für andere Übersetzungsprojekte erneut einzufügen.
Neben der eben geschilderten Aufgabe des c_pp-Projkts die Übersetzung von C++-Code in andere Programmiersprachen vorzubereiten, sind noch weitere Anwendungsmöglichkeiten denkbar. Beispielsweise könnte es dazu benutzt werden zu testen, ob Präprozessoranweisungen tatsächlich den Code erzeugen, den man erwartet. Tatsächlich gibt es hier so viele Fallstricke, dass ihnen im Gnu Präprozessor Manual ein langer Abschnitt gewidmet wurde:
Auch korrekt geschriebene Anweisungen haben den Nachteil, dass sie nur schwer zu debuggen sind. Scott Meyers gibt daher gleich im ersten Kapitel in seines viel beachteten Buchs: "Effective c++" den Rat: "prefer the compiler to the preprocessor". Eine weitere denkbare Anwendungsmöglichkeit für das c_pp-Projekt wäre es also, C++-Dateien real vorzuverarbeiten, d.h. sie in neue Dateien mit aufgelösten Präprozessoranweisungen umzuformen.
c_pp ist nahezu eine standardkonforme Implementierung der geforderten C99/C++ Vorprozessorfunktionalität. Die Abweichungen werden in den folgenden Anmerkungen erörtert, die wie die ausgezeichnete Einführung in den C Vorprozessor gegliedert sind:
Die Datei wird kontinuierlich gelesen, ohne sie zunächst in Zeilen umzubrechen, wie das andere Präprozessoren tun. Leerzeichen, Tabulatoren und Kommentare werden ignoriert
Trigraph-Sequenzen werden nicht ersetzt.
Backslashes `\' an Zeilenenden und nachfolgende Leerzeichen werden ausgelassen.
Alle Kommentare werden mit der Produktion "comment" durch einzelne Leerzeichen ersetzt. "comment" ist in c_pp als Einschluss-Produktion gesetzt. Dies ist eine spezielle TextTransformer-Funktion, die es erlaubt, Kommentare etc. einfach zu behandeln.
"Extrem verwirrende" Tricks, wie die Aufsplittung von `/*', `*/', und `//' in mehrere Zeilen durch eine Zeilenverlängerung, werden von c_pp nicht korrekt behandelt.
In TextTransformer-Projekten umfassen reguläre Ausdrücke für die auszulassenden Zeichen oft auch Zeilenenden. In der C-Präprozessor Grammatik haben Zeilenenden aber eine wichtige Rolle, so dass ihre möglichen Vorkommen explizit aufgeführt werden.
Gemäß dem 1999 C Standard können Bezeichner u.U. Buchstaben enthalten, die nicht zu den ASCII-Zeichenmenge gehören. c_pp kann solche Bezeichner nicht verarbeiten.
Eingeschlossene Benutzer- oder System-Header, wie
`#include "FILE"'
oder
`#include <FILE>'
werden beide durch den Ausdruck
PD_INCLUDE ::= #\s*include\s*("([^"]+)"|<([^>]+)>)
erkannt. Der erste Unterausdruck dieses Ausdrucks - in TextTransformer-Schreibweise: 'xState.str(1)' - liefert die einzuschließende Datei 'FILE'.
Immer, wenn c_pp eine Include-Anweisung findet, wird die Funktion
'scan_include_file'
aufgerufen. In dieser Funktion wird die Datei mit 'load_file' geladen und dann mit der Produktion 'header' auf die gleiche Art verarbeitet wie die ursprüngliche Datei. D.h. der vorverarbeitete Text der eingeschlossenen Datei wird an den Text angefügt, der aus der ursprünglichen Datei schon generiert wurde. Nach vollständiger Verarbeitung der eingeschlossenen Datei wird mit der Bearbeitung der einschließenden Datei fortgesetzt. Gibt es in der eingeschlossenen Datei ebenfalls include-Direktiven, so wird das Einschlussverfahren auf höherer Ebene analog ausgeführt. Ein Integer-Parameter für die aktuelle Ebene wird beim Aufruf von 'header' inkrementiert.
Ob eine Datei wirklich eingeschlossen werden soll, wird durch die Funktion 'ReallyInclude' gesteuert. Bei dem eingangs erwähnten Übersetzer von C++ nach Java wurden z.B. nur die unmittelbar zum Quelltext gehöriger Header-Datei eingeschlossen.
In c_pp wird zwischen System- und Benutzer-Headern nicht unterschieden. Nach dem Header wird in beiden Fällen gleichermaßen in einer Verzeichnisliste gesucht, die im vector
m_vIncludeDirs
gespeichert ist. Diese Liste kann beim Aufruf des Projekts als Konfigurations-Parameter übergeben werden. Je nach dem, wie das TextTransformer-Projekt ausgeführt wird, ist der Konfigurations-Parameter in den Projektoptionen (für die Arbeitsoberfläche der IDE), im Transformation-Manager oder als Kommandozeilen-Parameter zu setzen.
Im Konfigurations-String ist jedes Verzeichnis in eine Zeile zu schreiben. Z.B.
D:\Tetra\Projects\Divers\Cpp
C:\Programme\Borland\CBuilder6\Include
Diese Liste wird vor dem Strart des c_pp Präprozessors mit der Produktion 'IncludePaths' geparst, um den m_vIncludeDirs-vector zu füllen.
( SKIP {{ AddIncludeDir(trim_copy(xState.str())); }} ( EOL | EOF ) )*
Der Unterparser wird in der Init-Funktion aufgerufen:
IncludePaths(ConfigParam());
Außerdem wird mit
m_vIncludeDirs.push_back(SourceRoot());
das Wurzelverzeichnis des Quelltextes als Include-Pfad gesetzt.
m_mHeaderPaths
gespeichert. Der Präprozessor ließe sich dadurch beschleunigen, dass Dateien die bereits in dieser Liste enthalten sind, nicht nochmals geparst werden. Dies Verfahren ist jedoch nicht völlig korrekt, da sich zwischen zwei Einschlüssen der gleichen Datei die Menge der definierten Ausdrücke geändert haben kann. Die gleiche Datei kann daher bei nochmaliger verarbeitung ein anderes Resultat ergeben.
Makros sind Abkürzungen für Code-Fragmente, die durch die Präprozessor-Direktive '#define' definiert werden. Funktionsartige Makros enthalten Klammern und mögliche Argumente, während objektartige Makros einfache Bezeichner sind.
Makro-Definitionen werden in der Produktion 'definition' geparst. Sie beginnt mit dem Token
PD_DEFINE ::= #\s*define
Für objekt-artige Makros folgt hierauf ein einfacher Bezeichner 'ID'. Folgt hingegen ein Token
MACRO_DEF_BEGIN ::= (\w+)\(
so handelt es sich um eine funktionsartige Makrodefinition.
Entdeckt c_pp ein aufgerufenes Makro, so wird es expandiert: Makro Argumente werden ausgewertet, Argumente mit vorangestelltem '#' werden als String ausgegeben und Token können durch '##' verkettet werden. Variadische Makros (Variadic Macros) werden nicht unterstützt.
Mit undef können Makrodefinitionen wieder entfernt werden und makros können umdefiniert werden. (in c_pp erfolgt keine Warnmeldung, wenn die Neudefinition verschieden ist von der ursprünglichen.)
Die Expansion des Makros erfolgt ähnlich, wie von Kaiser auf macro_expansion_process.html beschrieben.
Die einfachste Art einer bedingten Gruppe ist:
#ifdef MACRO
CONTROLLED TEXT
#endif /* MACRO */
CONTROLLED TEXT wird vom Präprozessor genau dann ausgegeben, wenn MACRO definiert ist. Innerhalb von CONTROLLED TEXT können weitere Präprozessor Direktiven vorkommen. Sie werden nur dann ausgeführt, wenn die bedingung zutrifft. Bedingte Gruppierungen können ineinander verschachtelt werden.
Ein C-Präprozessor in Pascal von Dr. Hans-Peter Diettrich
http://members.aol.com/vbdis/
wave - Standard konforme Implementation der vorgeschriebenen C99/C++ C-Präprozessor Funktionalität in C++ von Hartmut Kaiser
http://www.boost.org/libs/wave/index.html
to the top |