Was ist ein Übersetzer? Programmiersprachen. Compiler und Interpreter. Algorithmus für einen einfachen Interpreter

Die konkreten Ausführenden von Programmiersprachen sind Übersetzer und Dolmetscher.

Übersetzer ist ein Programm, auf dessen Grundlage ein Computer in ihn eingegebene Programme in Maschinensprache umwandelt, da er nur Programme ausführen kann, die in der Sprache seines Prozessors geschrieben sind, und in einer anderen Sprache spezifizierte Algorithmen vor der Ausführung in Maschinensprache übersetzt werden müssen.

Übersetzer- ein Programm oder technisches Mittel, das eine Sendung ausstrahlt.

Ausstrahlung der Sendung- Umwandlung eines in einer der Programmiersprachen dargestellten Programms in ein Programm in einer anderen Sprache, das hinsichtlich der Ausführungsergebnisse dem ersten entspricht. Der Übersetzer führt in der Regel auch Fehlerdiagnosen durch, erstellt Bezeichnerwörterbücher, erstellt Programmtexte zum Drucken usw.

Die Sprache, in der das Eingabeprogramm präsentiert wird, wird aufgerufen Original Sprache und das Programm selbst - der Quellcode. Die Ausgabesprache wird Zielsprache bzw. Zielsprache genannt Zielsetzung Code. Der Zweck der Übersetzung besteht darin, einen Text so von einer Sprache in eine andere zu übertragen, dass er für den Empfänger des Textes verständlich ist. Bei Übersetzerprogrammen ist der Adressat technisches Gerät(Prozessor) oder Interpreterprogramm.

Übersetzer werden als Compiler oder Interpreter implementiert. Hinsichtlich der Arbeitsausführung unterscheiden sich Compiler und Interpreter erheblich.

Die Sprache der Prozessoren (Maschinencode) ist Low-Level. Es wird ein Übersetzer aufgerufen, der Programme in Maschinensprache umwandelt, die direkt vom Prozessor empfangen und ausgeführt werden Compiler.

Compiler(Englisch) Compiler(Compiler, Collector) liest das gesamte Programm, übersetzt es und erstellt eine vollständige Version des Programms in Maschinensprache, die dann ausgeführt wird. Das Ergebnis des Compilers ist eine binäre ausführbare Datei.

Der Vorteil des Compilers: Das Programm wird einmal kompiliert und es sind keine zusätzlichen Transformationen bei jeder Ausführung erforderlich. Dementsprechend ist auf dem Zielrechner, für den das Programm kompiliert wird, kein Compiler erforderlich. Nachteil: Ein separater Kompilierungsschritt verlangsamt das Schreiben und Debuggen und erschwert die Ausführung kleiner, einfacher oder einmaliger Programme.

Handelt es sich bei der Quellsprache um eine Assemblersprache (eine maschinensprachnahe Low-Level-Sprache), so wird der Compiler einer solchen Sprache aufgerufen Assembler.

Eine andere Implementierungsmethode besteht darin, das Programm mit auszuführen Dolmetscherüberhaupt keine Sendung.

Dolmetscher(Englisch) Dolmetscher- Interpreter, Interpreter) übersetzt und führt das Programm Zeile für Zeile aus.

Die Interpretersoftware modelliert eine Maschine, deren Abruf-Ausführungszyklus auf Anweisungen in Hochsprachen und nicht auf Maschinenanweisungen basiert. Diese Softwaresimulation erstellt eine virtuelle Maschine, die die Sprache implementiert. Dieser Ansatz heißt reine Interpretation. Für Sprachen mit einfacher Struktur (z. B. APL oder Lisp) wird in der Regel die reine Interpretation verwendet. Dolmetscher Befehlszeile Verarbeiten Sie Befehle in Skripten unter UNIX oder in Batchdateien (.bat) unter MS-DOS, meist auch im reinen Interpretationsmodus.

Der Vorteil eines reinen Dolmetschers: Durch das Fehlen von Zwischenhandlungen zur Übersetzung wird die Implementierung des Dolmetschers vereinfacht und die Bedienung auch im Dialogmodus komfortabler. Der Nachteil besteht darin, dass auf dem Zielrechner, auf dem das Programm ausgeführt werden soll, ein Interpreter vorhanden sein muss. Außerdem kommt es in der Regel zu einem mehr oder weniger deutlichen Geschwindigkeitsverlust. Und die Eigenschaft eines reinen Interpreters, dass Fehler im interpretierten Programm nur dann erkannt werden, wenn versucht wird, einen Befehl (oder eine Zeile) mit einem Fehler auszuführen, kann sowohl als Nachteil als auch als Vorteil angesehen werden.

Bei der Implementierung von Programmiersprachen gibt es Kompromisse zwischen Kompilierung und reiner Interpretation, wenn der Interpreter das Programm vor der Ausführung in eine Zwischensprache (z. B. in Bytecode oder P-Code) übersetzt, die für die Interpretation bequemer ist (d. h. wir sprechen von einem Dolmetscher mit eingebautem Übersetzer). Diese Methode heißt gemischte Umsetzung. Ein Beispiel für eine gemischtsprachige Implementierung ist Perl. Dieser Ansatz vereint sowohl die Vorteile eines Compilers und Interpreters (höhere Ausführungsgeschwindigkeit und Benutzerfreundlichkeit) als auch Nachteile (zusätzliche Ressourcen sind erforderlich, um ein Programm in eine Zwischensprache zu übersetzen und zu speichern; es muss ein Interpreter bereitgestellt werden, um das Programm auf dem Ziel auszuführen). Maschine). Ebenso wie beim Compiler ist dies auch bei einer gemischten Implementierung vor der Ausführung erforderlich Quelle enthielt keine Fehler (lexikalisch, syntaktisch und semantisch).

Mit der Zunahme der Computerressourcen und der Ausweitung heterogener Netzwerke (einschließlich des Internets), die Computer unterschiedlicher Art und Architektur verbinden, ist eine neue Art der Interpretation entstanden, bei der der Quellcode (oder Zwischencode) direkt zur Laufzeit in Maschinencode kompiliert wird , „im Fluge.“ Bereits kompilierte Codeabschnitte werden zwischengespeichert, sodass sie bei einem erneuten Zugriff sofort die Kontrolle übernehmen, ohne dass eine Neukompilierung erforderlich ist. Dieser Ansatz heißt dynamische Zusammenstellung.

Der Vorteil der dynamischen Kompilierung besteht darin, dass die Geschwindigkeit der Programminterpretation mit der Geschwindigkeit der Programmausführung in herkömmlichen kompilierten Sprachen vergleichbar wird, während das Programm selbst unabhängig von der Zielplattform in einer einzigen Form gespeichert und verteilt wird. Der Nachteil ist ein höherer Implementierungsaufwand und ein größerer Ressourcenbedarf als bei einfachen Compilern oder reinen Interpretern.

Diese Methode eignet sich gut für Webanwendungen. Dementsprechend ist die dynamische Kompilierung erschienen und wird in Java-Implementierungen bis zu einem gewissen Grad unterstützt. NET Framework, Perl, Python.

Sobald ein Programm kompiliert ist, sind weder der Quellcode des Programms noch ein Compiler erforderlich, um das Programm auszuführen. Gleichzeitig muss das vom Interpreter verarbeitete Programm bei jedem Programmstart erneut in Maschinensprache übersetzt werden. Das heißt, die Quelldatei ist direkt ausführbar.

Kompilierte Programme laufen schneller, interpretierte Programme lassen sich jedoch leichter reparieren und ändern.

Jede spezifische Sprache ist entweder auf Kompilation oder Interpretation ausgerichtet – je nachdem, für welchen Zweck sie erstellt wurde. Beispielsweise wird C++ normalerweise zur Lösung recht komplexer Probleme verwendet, bei denen die Programmgeschwindigkeit wichtig ist. Daher wird diese Sprache mithilfe eines Compilers implementiert.

Um eine höhere Arbeitsgeschwindigkeit von Programmen in interpretierten Programmiersprachen zu erreichen, kann die Übersetzung in Zwischenbytecode verwendet werden. Die Sprachen, die diesen Trick ermöglichen, sind Java, Python und einige andere Programmiersprachen.

Algorithmus für einen einfachen Interpreter:

2. Analysieren Sie die Anweisungen und legen Sie die geeigneten Maßnahmen fest.

3. geeignete Maßnahmen ergreifen;

4. Wenn die Programmabbruchbedingung nicht erreicht wird, lesen Sie die folgenden Anweisungen und fahren Sie mit Schritt 2 fort

Programmiersprachen können in kompilierte und interpretierte Sprachen unterteilt werden.

Ein Programm in einer kompilierten Sprache wird mithilfe eines speziellen Compilerprogramms in einen Befehlssatz für konvertiert (kompiliert). dieser Art Prozessor (Maschinencode) und wird dann in ein ausführbares Modul geschrieben, das zur Ausführung als separates Programm gestartet werden kann. Mit anderen Worten übersetzt der Compiler den Programmquellcode aus einer höheren Programmiersprache in Binärcodes von Prozessoranweisungen.

Wenn ein Programm in einer interpretierten Sprache geschrieben ist, führt (interpretiert) der Interpreter den Quelltext direkt aus, ohne dass eine vorherige Übersetzung erforderlich ist. In diesem Fall bleibt das Programm in der Originalsprache und kann nicht ohne Dolmetscher gestartet werden. Wir können sagen, dass ein Computerprozessor ein Interpreter von Maschinencode ist.

Kurz gesagt übersetzt der Compiler den Quellcode eines Programms sofort und vollständig in Maschinensprache und erstellt so ein separates ausführbares Programm, und der Interpreter führt den Quelltext aus, während das Programm ausgeführt wird.

Die Einteilung in kompilierte und interpretierte Sprachen ist etwas willkürlich. Sie können also für jede traditionell kompilierte Sprache wie Pascal einen Interpreter schreiben. Darüber hinaus führen die meisten modernen „reinen“ Interpreter Sprachkonstrukte nicht direkt aus, sondern kompilieren sie in eine Zwischendarstellung auf hoher Ebene (z. B. mit Variablendereferenzierung und Makroerweiterung).

Ein Compiler kann für jede interpretierte Sprache erstellt werden – beispielsweise kann die Sprache Lisp, die nativ interpretiert wird, ohne Einschränkungen kompiliert werden. Während der Programmausführung generierter Code kann auch während der Ausführung dynamisch kompiliert werden.

Im Allgemeinen laufen kompilierte Programme schneller und benötigen keine zusätzliche Programme, da sie bereits in Maschinensprache übersetzt wurden. Gleichzeitig muss der Programmtext jedes Mal, wenn er geändert wird, neu kompiliert werden, was zu Schwierigkeiten bei der Entwicklung führt. Darüber hinaus kann das kompilierte Programm nur auf demselben Computertyp und in der Regel unter demselben Betriebssystem ausgeführt werden, für das der Compiler entwickelt wurde. Um eine ausführbare Datei für einen anderen Maschinentyp zu erstellen, ist eine neue Kompilierung erforderlich.

Interpretierte Sprachen verfügen über einige spezifische Zusatzfunktionen (siehe oben), außerdem können darin enthaltene Programme sofort nach der Änderung ausgeführt werden, was die Entwicklung erleichtert. Ein Programm in einer interpretierten Sprache kann oft ohne zusätzlichen Aufwand auf verschiedenen Maschinentypen und Betriebssystemen ausgeführt werden.

Allerdings laufen interpretierte Programme deutlich langsamer als kompilierte und können ohne ein zusätzliches Interpreterprogramm nicht ausgeführt werden.

Einige Sprachen wie Java und C# fallen zwischen kompiliert und interpretiert. Das Programm wird nämlich nicht in Maschinensprache kompiliert, sondern in maschinenunabhängigen Low-Level-Code, Bytecode. Anschließend wird der Bytecode ausgeführt virtuelle Maschine. Bei der Ausführung von Bytecode wird in der Regel Interpretation eingesetzt, einzelne Teile davon können jedoch direkt während der Programmausführung mittels Just-in-Time-Kompilierung (JIT) in Maschinencode übersetzt werden, um das Programm zu beschleunigen. Für Java wird der Bytecode virtuell ausgeführt Java-Maschine(Java Virtual Machine, JVM), für C# – Common Language Runtime.

Dieser Ansatz ermöglicht es Ihnen gewissermaßen, die Vorteile von Interpretern und Compilern zu nutzen. Erwähnenswert ist auch die ursprüngliche Forth-Sprache, die sowohl über einen Interpreter als auch über einen Compiler verfügt.

Da in einer Programmiersprache geschriebene Texte für einen Computer unverständlich sind, müssen sie in Maschinencode übersetzt werden. Diese Übersetzung eines Programms aus einer Programmiersprache in eine Maschinencodesprache wird Übersetzung genannt und wird von speziellen Programmen – Übersetzern – durchgeführt.

Übersetzer – ein Dienstprogramm, das das in der Eingabeprogrammiersprache bereitgestellte Quellprogramm in umwandelt Arbeitsprogramm, dargestellt in einer Objektsprache.

Derzeit werden Übersetzer in drei Hauptgruppen eingeteilt: Assembler, Compiler und Interpreter.

Ein Assembler ist ein Systemdienstprogramm, das symbolische Strukturen in maschinensprachliche Befehle umwandelt. Eine Besonderheit von Assemblern besteht darin, dass sie eine wörtliche Übersetzung einer symbolischen Anweisung in eine Maschinenanweisung durchführen. Daher soll die Assemblersprache (auch Autocode genannt) die Wahrnehmung des Computerbefehlssystems erleichtern und die Programmierung in diesem Befehlssystem beschleunigen. Für einen Programmierer ist es viel einfacher, sich die mnemonische Bezeichnung von Maschinenanweisungen zu merken als ihren Binärcode.

Gleichzeitig enthält die Assemblersprache neben Analoga von Maschinenbefehlen viele zusätzliche Anweisungen, die insbesondere die Verwaltung von Computerressourcen, das Schreiben sich wiederholender Fragmente und die Erstellung von Programmen mit mehreren Modulen erleichtern. Daher ist die Ausdruckskraft der Sprache viel reicher als nur eine symbolische Codierungssprache, was die Programmiereffizienz erheblich verbessert.

Ein Compiler ist ein Dienstprogramm, das ein in der Quellprogrammiersprache geschriebenes Programm in Maschinensprache übersetzt. Genau wie ein Assembler konvertiert ein Compiler ein Programm von einer Sprache in eine andere (meistens in die Sprache eines bestimmten Computers). Gleichzeitig unterscheiden sich ausgangssprachliche Befehle hinsichtlich Organisation und Leistungsfähigkeit erheblich von maschinensprachlichen Befehlen. Es gibt Sprachen, in denen ein Befehl der Ausgangssprache in 7-10 Maschinenbefehle übersetzt wird. Es gibt jedoch auch Sprachen, in denen jeder Befehl 100 oder mehr Maschinenbefehle haben kann (z. B. Prolog). Darüber hinaus verwenden die Quellsprachen häufig eine strikte Datentypisierung, die durch ihre vorläufige Beschreibung erfolgt. Beim Programmieren kommt es möglicherweise nicht darauf an, einen Algorithmus zu programmieren, sondern darauf, sorgfältig über Datenstrukturen oder Klassen nachzudenken. Der Prozess der Übersetzung aus solchen Sprachen wird normalerweise als Kompilierung bezeichnet, und die Quellsprachen werden normalerweise als Programmiersprachen auf hoher Ebene (oder Hochsprachen) klassifiziert. Die Abstraktion einer Programmiersprache vom Computerbefehlssystem führte zur eigenständigen Schaffung einer Vielzahl von Sprachen, die auf die Lösung spezifischer Probleme ausgerichtet sind. Für wissenschaftliche Berechnungen, wirtschaftliche Berechnungen, den Zugriff auf Datenbanken und andere sind Sprachen erschienen.

Dolmetscher – ein Programm oder Gerät, das eine Operator-für-Operator-Übersetzung und Ausführung des Quellprogramms durchführt. Im Gegensatz zu einem Compiler erzeugt ein Interpreter kein Maschinensprachenprogramm als Ausgabe. Sobald ein Befehl in der Quellsprache erkannt wird, führt er ihn sofort aus. Sowohl Compiler als auch Interpreter verwenden dieselben Methoden zur Analyse des Quellcodes eines Programms. Mit dem Interpreter können Sie jedoch mit der Datenverarbeitung beginnen, nachdem Sie auch nur einen Befehl geschrieben haben. Dadurch wird der Prozess der Programmentwicklung und des Debuggens flexibler. Darüber hinaus können Sie durch das Fehlen von Ausgabemaschinencode Unordnung vermeiden Externe Geräte zusätzliche Dateien, und der Interpreter selbst kann ganz einfach an jede Maschinenarchitektur angepasst werden, indem er nur einmal in einer weit verbreiteten Programmiersprache entwickelt wird. Daher haben sich interpretierte Sprachen wie Java Script und VB Script weit verbreitet. Der Nachteil von Interpretern ist die geringe Geschwindigkeit der Programmausführung. Typischerweise laufen interpretierte Programme 50 bis 100 Mal langsamer als native Programme.

Ein Emulator ist ein Programm oder Software- und Hardware-Tool, das die Möglichkeit bietet, ohne Neuprogrammierung auf einem bestimmten Computer ein Programm auszuführen, das Codes oder Methoden zur Ausführung von Vorgängen verwendet, die sich von denen des jeweiligen Computers unterscheiden. Ein Emulator ähnelt einem Interpreter darin, dass er ein in einer bestimmten Sprache geschriebenes Programm direkt ausführt. Meistens handelt es sich jedoch um Maschinensprache oder Zwischencode. Beide stellen Anweisungen im Binärcode dar, die unmittelbar nach Erkennung des Operationscodes ausgeführt werden können. Im Gegensatz zu Textprogrammen ist es nicht erforderlich, die Programmstruktur zu kennen oder Operanden auszuwählen.

Emulatoren werden häufig für verschiedene Zwecke eingesetzt. Beispielsweise wird bei der Entwicklung neuer Computersysteme zunächst ein Emulator erstellt, der Programme ausführt, die für Computer entwickelt wurden, die es noch nicht gibt. Auf diese Weise können Sie das Befehlssystem evaluieren und Grundlagen entwickeln Software noch bevor das entsprechende Equipment erstellt wird.

Sehr oft wird ein Emulator verwendet, um alte Programme auf neuen Computern auszuführen. In der Regel sind neuere Computer schneller und verfügen über bessere Peripheriegeräte. Dadurch können Sie ältere Programme effizienter emulieren, als sie auf älteren Computern auszuführen.

Ein Transcoder ist ein Programm oder Softwaregerät, das in der Maschinensprache eines Computers geschriebene Programme in Programme in der Maschinensprache eines anderen Computers übersetzt. Wenn der Emulator ein weniger intelligentes Analogon des Interpreters ist, verhält sich der Transcoder im Verhältnis zum Compiler in gleicher Funktion. In ähnlicher Weise wird Quell- (und normalerweise binärer) Maschinencode oder eine Zwischendarstellung mit einer einzigen Anweisung und ohne allgemeine Analyse der Struktur des Quellprogramms in anderen ähnlichen Code umgewandelt. Transcoder sind nützlich, wenn Programme von einer Computerarchitektur auf eine andere übertragen werden. Sie können auch verwendet werden, um hochsprachliche Programmtexte aus vorhandenem Binärcode zu rekonstruieren.

Ein Makroprozessor ist ein Programm, das eine Zeichenfolge durch eine andere ersetzt. Dies ist eine Art Compiler. Es generiert Ausgabetext durch die Verarbeitung spezieller Einfügungen im Quelltext. Diese Einfügungen sind auf besondere Weise konzipiert und gehören zu Konstrukten einer Sprache, die Makrosprache genannt wird. Makroprozessoren werden oft als Add-ons zu Programmiersprachen eingesetzt und erhöhen die Funktionalität von Programmiersystemen. Fast jeder Assembler enthält einen Makroprozessor, der die Effizienz bei der Entwicklung von Maschinenprogrammen erhöht. Solche Programmiersysteme werden üblicherweise Makroassembler genannt.

Makroprozessoren werden auch bei Hochsprachen verwendet. Sie erhöhen die Funktionalität von Sprachen wie PL/1, C, C++. Makroprozessoren werden vor allem in C und C++ häufig eingesetzt, um das Schreiben von Programmen zu erleichtern. Makroprozessoren verbessern die Programmiereffizienz, ohne die Syntax oder Semantik der Sprache zu ändern.

Syntax ist eine Reihe von Regeln einer Sprache, die die Bildung ihrer Elemente bestimmen. Mit anderen Worten handelt es sich hierbei um eine Reihe von Regeln für die Bildung semantisch bedeutsamer Symbolfolgen in einer bestimmten Sprache. Die Syntax wird mithilfe von Regeln angegeben, die die Konzepte einer Sprache beschreiben. Beispiele für Konzepte sind: Variable, Ausdruck, Operator, Prozedur. Die Reihenfolge der Konzepte und ihre akzeptable Verwendung in den Regeln werden syntaktisch festgelegt Richtige Strukturen, Programme bilden. Durch die Syntax wird die Hierarchie der Objekte definiert und nicht die Art und Weise, wie sie miteinander interagieren. Beispielsweise kann eine Anweisung nur in einer Prozedur vorkommen, ein Ausdruck in einer Anweisung, eine Variable kann aus einem Namen und optionalen Indizes bestehen usw. Die Syntax ist nicht mit solchen Phänomenen im Programm wie „Springen zu einer nicht vorhandenen Bezeichnung“ oder „Eine Variable mit dem angegebenen Namen ist nicht definiert“ verbunden. Das ist es, was die Semantik bewirkt.

Semantik – Regeln und Bedingungen, die die Beziehungen zwischen Sprachelementen und ihren semantischen Bedeutungen sowie die Interpretation der sinnvollen Bedeutung syntaktischer Konstruktionen der Sprache bestimmen. Objekte einer Programmiersprache werden nicht nur nach einer bestimmten Hierarchie im Text platziert, sondern sind zusätzlich durch andere Konzepte miteinander verbunden, die verschiedene Assoziationen bilden. Beispielsweise hat eine Variable, für die die Syntax nur in Deklarationen und einigen Anweisungen eine gültige Position definiert, einen bestimmten Typ, kann mit einer begrenzten Anzahl von Operationen verwendet werden, hat eine Adresse und eine Größe und muss vorher deklariert werden im Programm verwendet werden.

Ein Parser ist eine Compiler-Komponente, die Quellanweisungen auf Übereinstimmung mit den syntaktischen Regeln und der Semantik einer bestimmten Programmiersprache überprüft. Trotz seines Namens prüft der Analysator sowohl Syntax als auch Semantik. Es besteht aus mehreren Blöcken, von denen jeder seine eigenen Probleme löst. Darauf wird bei der Beschreibung der Struktur des Übersetzers näher eingegangen. Übersetzer-Compiler-Sprachprogrammierung

Jeder Übersetzer führt die folgenden Hauptaufgaben aus:

  • - analysiert das übersetzte Programm und stellt insbesondere fest, ob es Syntaxfehler enthält;
  • - generiert ein Ausgabeprogramm (oft als Objektprogramm bezeichnet) in Maschinenbefehlssprache;
  • - reserviert Speicher für das Objektprogramm.1.1 Interpreter

Ein oft genannter Vorteil der interpretativen Implementierung besteht darin, dass sie einen „Sofortmodus“ ermöglicht. Im Direktmodus können Sie dem Computer eine Aufgabe wie PRINT 3.14159*3/2.1 stellen und Ihnen die Antwort zurücksenden, sobald Sie die EINGABETASTE drücken (dies ermöglicht Ihnen, einen 3.000-Dollar-Computer als 10-Dollar-Rechner zu verwenden). Darüber hinaus verfügen Interpreter über spezielle Attribute, die das Debuggen erleichtern. Sie können beispielsweise die Verarbeitung eines Interpreterprogramms unterbrechen, den Inhalt bestimmter Variablen anzeigen, das Programm überfliegen und dann mit der Ausführung fortfahren.

Was Programmierer an Dolmetschern am meisten schätzen, ist die Möglichkeit, eine schnelle Antwort zu erhalten. Eine Kompilierung ist hier nicht erforderlich, da der Interpreter immer bereit ist, in Ihr Programm einzugreifen. Geben Sie RUN ein und das Ergebnis gehört Ihnen Letzte Bearbeitung erscheint auf dem Bildschirm.

Allerdings haben Dolmetschersprachen Nachteile. Es ist beispielsweise erforderlich, jederzeit eine Kopie des Interpreters im Speicher zu haben, während viele der Fähigkeiten des Interpreters und damit seine Fähigkeiten für die Ausführung eines bestimmten Programms möglicherweise nicht erforderlich sind.

Ein subtiler Nachteil von Dolmetschern besteht darin, dass sie dazu neigen, einen guten Programmierstil zu verhindern. Da Kommentare und andere formalisierte Details einen erheblichen Teil des Programmspeichers beanspruchen, werden sie in der Regel nicht verwendet. Der Teufel ist weniger wütend als ein Programmierer, der im BASIC-Interpreter arbeitet und versucht, ein 120-KByte-Programm in einen 60-KByte-Speicher zu bekommen. Aber das Schlimmste ist, dass die Dolmetscher langsam sind.

Sie verbringen zu viel Zeit damit, herauszufinden, was zu tun ist, anstatt die Arbeit tatsächlich zu erledigen. Beim Ausführen von Programmanweisungen muss der Interpreter zunächst jede Anweisung scannen, um deren Inhalt zu lesen (was verlangt diese Person von mir?) und dann die angeforderte Operation ausführen. Operatoren in Schleifen werden übermäßig gescannt.

Betrachten Sie das Programm: im Interpreter BASIC 10 FOR N=1 TO 1000 20 PRINT N,SQR(N) 30 NEXT N Wenn Sie dieses Programm zum ersten Mal durchlaufen, muss der BASIC-Interpreter herausfinden, was Zeile 20 bedeutet:

  • 1. Konvertieren Sie die numerische Variable N in eine Zeichenfolge
  • 2. Senden Sie eine Zeichenfolge an den Bildschirm
  • 3. Gehen Sie zum nächsten Druckbereich
  • 4. Berechnen Sie die Quadratwurzel von N
  • 5. Konvertieren Sie das Ergebnis in einen String
  • 6. Senden Sie eine Zeichenfolge an den Bildschirm

Während des zweiten Durchlaufs des Zyklus wird die gesamte Lösung noch einmal wiederholt, da alle Ergebnisse des Studiums dieser Zeile vor einigen Millisekunden völlig vergessen sind. Und so weiter für alle nächsten 998 Durchgänge. Wenn Sie die Scan-/Verständnisphase irgendwie von der Ausführungsphase trennen könnten, hätten Sie natürlich mehr schnelles Programm. Und genau dafür sind Compiler da.


4. Grundprinzipien der Konstruktion von Übersetzern. Übersetzer, Compiler und Dolmetscher – das allgemeine Arbeitsschema. Moderne Compiler und Interpreter.

Grundprinzipien der Übersetzerkonstruktion.

Übersetzer, Compiler, Dolmetscher – das allgemeine Arbeitsschema.

Definition eines Übersetzers, Compilers, Interpreters

Lassen Sie uns zunächst ein paar Definitionen geben – was genau sind die bereits mehrfach erwähnten Übersetzer und Compiler.

Formale Definition eines Übersetzers

Übersetzer ist ein Programm, das ein Eingabeprogramm in der Quellsprache (Eingabesprache) in ein äquivalentes Ausgabeprogramm in der resultierenden Sprache (Ausgabesprache) übersetzt. In dieser Definition kommt das Wort „Programm“ dreimal vor, und dies ist kein Fehler oder eine Tautologie. Tatsächlich sind an der Arbeit des Übersetzers immer drei Programme beteiligt.

Erstens ist der Übersetzer selbst Programm 1 – er ist normalerweise Teil der Systemsoftware eines Computersystems. Das heißt, ein Übersetzer ist eine Software; er besteht aus einer Reihe von Maschinenanweisungen und Daten und wird wie alle anderen Programme innerhalb des Betriebssystems (OS) von einem Computer ausgeführt. Alle Komponenten des Übersetzers sind Programmfragmente oder Module mit eigenen Ein- und Ausgabedaten.

Zweitens sind die Eingabedaten für den Übersetzer der Text des Eingabeprogramms – eine bestimmte Folge von Sätzen in der Eingabeprogrammiersprache. Dabei handelt es sich in der Regel um eine Symboldatei, die Datei muss jedoch Programmtext enthalten, der die syntaktischen und semantischen Anforderungen der Eingabesprache erfüllt. Darüber hinaus trägt diese Datei eine gewisse Bedeutung, die durch die Semantik der Eingabesprache bestimmt wird.

Drittens ist die Ausgabe des Übersetzers der Text des resultierenden Programms. Das resultierende Programm wird nach syntaktischen Regeln erstellt, die in der Ausgabesprache des Übersetzers festgelegt sind, und seine Bedeutung wird durch die Semantik der Ausgabesprache bestimmt. Eine wichtige Anforderung bei der Definition eines Übersetzers ist die Gleichwertigkeit der Eingabe- und Ausgabeprogramme. Die Äquivalenz zweier Programme bedeutet die Übereinstimmung ihrer Bedeutung aus Sicht der Semantik der Eingabesprache (für das Quellprogramm) und der Semantik der Ausgabesprache (für das resultierende Programm). Ohne die Erfüllung dieser Anforderung verliert der Übersetzer selbst jeglichen praktischen Sinn.

Um einen Übersetzer zu erstellen, müssen Sie also zunächst die Eingabe- und Ausgabesprachen auswählen. Im Hinblick auf die Umwandlung von Sätzen der Eingabesprache in äquivalente Sätze der Ausgabesprache fungiert der Übersetzer als Übersetzer. Beispielsweise unterscheidet sich die Übersetzung eines Programms aus der C-Sprache in die Assemblersprache im Wesentlichen nicht von der Übersetzung beispielsweise aus dem Russischen ins Englische, mit dem einzigen Unterschied, dass die Komplexität der Sprachen etwas anders ist (weshalb es keine Übersetzer aus der natürlichen Sprache gibt). Sprachen – siehe Abschnitt „Klassifikation“ von Sprachen und Grammatiken“, Kapitel 9). Daher bedeutet das Wort „Übersetzer“ selbst (englisch: translator) „Übersetzer“.

Das Ergebnis der Arbeit des Übersetzers ist das resultierende Programm, jedoch nur, wenn der Text des Quellprogramms korrekt ist – keine Fehler in Bezug auf Syntax und Semantik der Eingabesprache enthält. Wenn das Quellprogramm fehlerhaft ist (mindestens einen Fehler enthält), ist das Ergebnis des Übersetzers eine Fehlermeldung (normalerweise mit zusätzlichen Erläuterungen und einem Hinweis auf die Position des Fehlers im Quellprogramm). In diesem Sinne ist ein Übersetzer vergleichbar mit einem Übersetzer, beispielsweise aus dem Englischen, dem ein falscher Text vorgelegt wurde.

Es ist theoretisch möglich, einen Übersetzer mithilfe von Hardware zu implementieren. Der Autor ist auf solche Entwicklungen gestoßen, ihre breite praktische Anwendung ist jedoch nicht bekannt. In diesem Fall können alle Komponenten des Übersetzers in Form von Hardware und deren Fragmenten implementiert werden – dann kann die Erkennungsschaltung eine völlig praktische Implementierung erhalten!

Definition eines Compilers.

Der Unterschied zwischen einem Compiler und einem Übersetzer

Neben dem Begriff „Übersetzer“ ist auch der in seiner Bedeutung ähnliche Begriff „Compiler“ weit verbreitet.

Compiler - Hierbei handelt es sich um einen Übersetzer, der ein Quellprogramm in ein entsprechendes Objektprogramm in Maschinenbefehlssprache oder Assemblersprache übersetzt.

Somit unterscheidet sich ein Compiler von einem Übersetzer nur dadurch, dass sein resultierendes Programm immer in Maschinencode oder Assemblersprache geschrieben sein muss. Das resultierende Übersetzerprogramm kann im Allgemeinen in jeder Sprache geschrieben werden – beispielsweise ist ein Übersetzer von Programmen von Pascal nach C möglich. Dementsprechend ist jeder Compiler ein Übersetzer, aber nicht umgekehrt – nicht jeder Übersetzer wird ein Compiler sein. Beispielsweise ist der oben erwähnte Pascal-zu-C-Übersetzer kein Compiler 1 .

Das Wort „Compiler“ selbst kommt von Englischer Begriff„Compiler“ („Compiler“, „Linker“). Offenbar verdankt der Begriff seinen Ursprung der Fähigkeit von Compilern, Objektprogramme auf der Grundlage von Quellprogrammen zu komponieren.

Das resultierende Compilerprogramm wird als „Objektprogramm“ oder „Objektcode“ bezeichnet. Die Datei, in die es geschrieben wird, wird üblicherweise als „Objektdatei“ bezeichnet. Selbst wenn das resultierende Programm in Maschinenbefehlssprache generiert wird, besteht ein erheblicher Unterschied zwischen einem Objektprogramm (Objektdatei) und einem ausführbaren Programm (ausführbare Datei). Ein von einem Compiler generiertes Programm kann nicht direkt auf einem Computer ausgeführt werden, da es nicht an einen bestimmten Speicherbereich gebunden ist, in dem sich sein Code und seine Daten befinden sollen (weitere Einzelheiten finden Sie im Abschnitt „Funktionsprinzipien von Programmiersystemen“, Kapitel 15) 2.

Compiler sind natürlich die häufigste Art von Übersetzern (viele Leute halten sie für die einzige Art von Übersetzern, obwohl das nicht stimmt). Sie haben die breiteste praktische Anwendung, was auf die weit verbreitete Verwendung aller Arten von Programmiersprachen zurückzuführen ist. Im Folgenden wird immer von Compilern die Rede sein, was bedeutet, dass das Ausgabeprogramm in geschrieben wird

Natürlich werden Übersetzer und Compiler, wie alle anderen Programme auch, von einer Person (Menschen) entwickelt – normalerweise einer Gruppe von Entwicklern. Im Prinzip könnten sie es direkt in Maschinenbefehlssprache erstellen, aber die Menge an Code und Daten moderner Compiler ist so groß, dass es praktisch unmöglich ist, sie in Maschinenbefehlssprache in angemessener Zeit und mit vertretbaren Arbeitskosten zu erstellen. Daher werden fast alle modernen Compiler auch mit Compilern erstellt (normalerweise spielen frühere Versionen von Compilern desselben Herstellers diese Rolle). Und in dieser Eigenschaft ist der Compiler bereits ein Ausgabeprogramm für einen anderen Compiler, das nicht besser oder schlechter ist als alle anderen generierten Ausgabeprogramme 2.

Definition eines Dolmetschers. Unterschied zwischen Dolmetschern und Übersetzern

Neben den ähnlichen Konzepten „Übersetzer“ und „Compiler“ gibt es ein grundlegend anderes Konzept eines Interpreters.

Dolmetscher - Es handelt sich um ein Programm, das ein Eingabeprogramm in der Quellsprache entgegennimmt und ausführt.

Im Gegensatz zu Übersetzern generieren Interpreter kein resultierendes Programm (oder überhaupt keinen resultierenden Code) – und das ist der grundlegende Unterschied zwischen ihnen. Der Interpreter analysiert wie der Übersetzer den Text des Quellprogramms. Es generiert jedoch nicht das resultierende Programm, sondern führt das ursprüngliche Programm sofort entsprechend seiner Bedeutung aus, die durch die Semantik der Eingabesprache gegeben ist. Somit ist das Ergebnis des Interpreters das durch die Bedeutung des Quellprogramms spezifizierte Ergebnis, wenn dieses Programm korrekt ist, oder eine Fehlermeldung, wenn das Quellprogramm falsch ist.

Um das Quellprogramm auszuführen, muss der Interpreter es natürlich irgendwie in Maschinencodesprache umwandeln, da es sonst unmöglich ist, Programme auf einem Computer auszuführen. Er tut dies, aber die resultierenden Maschinencodes sind nicht zugänglich – sie sind für den Benutzer des Interpreters nicht sichtbar. Diese Maschinencodes werden vom Interpreter generiert, ausgeführt und zerstört.

1 Besonders hervorzuheben ist, dass in modernen Programmiersystemen mittlerweile Compiler auftauchen, bei denen das resultierende Programm nicht in Maschinenbefehlssprache oder Assemblersprache, sondern in einer Zwischensprache erstellt wird. Diese Zwischensprache selbst kann nicht direkt auf einem Computer ausgeführt werden, sondern erfordert einen speziellen Zwischeninterpreter, um darauf geschriebene Programme auszuführen. Obwohl in diesem Fall der Begriff „Übersetzer“ wahrscheinlich korrekter wäre, wird in der Literatur der Begriff „Compiler“ verwendet, da es sich bei einer Zwischensprache um eine Sprache auf sehr niedrigem Niveau handelt, die Maschinenanweisungen und Assemblersprachen ähnelt.

Hier stellt sich die uralte „Henne und Ei“-Frage. Natürlich wurden in der ersten Generation die allerersten Compiler direkt auf Maschinenanweisungen geschrieben, aber mit dem Aufkommen der Compiler entfernte man sich von dieser Praxis. Selbst die kritischsten Teile von Compilern werden zumindest in Assemblersprache erstellt – und diese werden auch vom Compiler verarbeitet. werden nach Bedarf angepasst (je nach Anforderung einer bestimmten Implementierung) des Interpreters. Der Benutzer sieht das Ergebnis der Ausführung dieser Codes – dies ist das Ergebnis der Ausführung des Quellprogramms (die Voraussetzung für die Gleichwertigkeit des Quellprogramms und der generierten Maschinencodes muss in diesem Fall (bedingt) erfüllt sein).

Probleme im Zusammenhang mit der Implementierung von Interpretern und deren Unterschied zu Compilern werden im entsprechenden Abschnitt ausführlicher erörtert.

Zweck von Übersetzern, Compilern und Dolmetschern. Implementierungsbeispiele

Die ersten Programme, die für Computer der ersten Generation erstellt wurden, wurden direkt in Maschinencodesprache geschrieben. Es war wirklich eine verdammt gute Arbeit. Es wurde sofort klar, dass ein Mensch die Sprache der Maschinenbefehle nicht beherrschen sollte und kann, auch wenn er ein Computerspezialist ist. UM; Allerdings waren alle Versuche, einem Computer das Sprechen menschlicher Sprachen beizubringen, erfolgreich und werden wahrscheinlich nie erfolgreich sein (wofür es bestimmte o6itive Gründe gibt, die im ersten Kapitel dieses Handbuchs erörtert wurden).

Seitdem ist die gesamte Entwicklung von Computersoftware untrennbar mit der Entstehung und Entwicklung von Compilern verbunden.

Die ersten Compiler waren Compiler aus Assemblersprachen oder, wie sie genannt wurden, mnemonischen Codes. Mnemonische Codes verwandelten die „Philkin-Grammatik“ der Sprache der Maschinenbefehle in eine Sprache monischer (meist englischer) Notationen für diese Befehle, die für einen Spezialisten mehr oder weniger verständlich sind. (Das Erstellen von Programmen ist bereits viel einfacher geworden, aber kein einziger Computer ist in der Lage, die Mnems (Assemblersprache) selbst auszuführen. Dementsprechend entstand die Notwendigkeit, Compiler zu erstellen. Diese Compiler sind grundlegend, spielen aber weiterhin eine wichtige Rolle in der Programmierung Systeme heute. Weitere Details zu Assemblersprache und Compilern gibt es eine Geschichte dazu: weiter unten im entsprechenden Abschnitt.

Der nächste Schritt war die Schaffung von Hochsprachen. Hochsprachen (dazu zählen die meisten Programmiersprachen) stellen eine Art Zwischenglied zwischen rein formalen Sprachen und den Sprachen der natürlichen Kommunikation zwischen Menschen dar. Von der ersten erhielten sie eine strikte Malisierung der syntaktischen Struktur von Sätzen der Sprache, von der zweiten - ein bedeutender Teil des Wortschatzes, die Semantik grundlegender Konstruktionen und Ausdrücke (mit Elementen mathematischer Operationen, die aus der Algebra stammten).

Das Aufkommen von Hochsprachen vereinfachte den Programmierprozess erheblich, reduzierte ihn jedoch nicht auf die „Hausfrauenebene“, wie einige Autoren zu Beginn der Programmiersprachen arrogant behaupteten 1 . Es gab nur wenige solcher Sprachen, dann Dutzende, und heute sind es wahrscheinlich mehr als hundert. Ein Ende dieses Prozesses ist nicht in Sicht. Dennoch dominieren immer noch Computer der traditionellen „Neumann“-Architektur, die nur Maschinenanweisungen verstehen können, sodass die Frage der Erstellung von Compilern weiterhin relevant ist.

Sobald ein massiver Bedarf an der Entwicklung von Compilern entstand, begann sich eine spezielle Theorie zu entwickeln. Im Laufe der Zeit fand es praktische Anwendungen in den vielen Compilern, die erstellt wurden. Compiler wurden und werden nicht nur für neue, sondern auch für seit langem bekannte Sprachen erstellt. Viele Hersteller, von bekannten, renommierten Unternehmen (wie Microsoft oder Inprise) bis hin zu wenig bekannten Autorenteams, bringen immer mehr neue Compiler-Beispiele auf den Markt. Dafür gibt es mehrere Gründe, die im Folgenden erörtert werden.

Da schließlich die meisten theoretischen Aspekte im Bereich der Compiler ihre praktische Umsetzung erhielten (und dies geschah, das muss gesagt werden, ziemlich schnell, in den späten 60er Jahren), folgte die Entwicklung der Compiler dem Weg ihrer Benutzerfreundlichkeit - der Benutzer, der Entwickler von Programmen in Sprachen hohen Niveaus. Der logische Abschluss dieses Prozesses war die Schaffung von Programmiersystemen - Softwaresysteme, die neben den Compilern selbst viele verwandte Softwarekomponenten vereinen. Nach ihrem Aufkommen eroberten Programmiersysteme schnell den Markt und dominieren ihn heute größtenteils (tatsächlich sind eigenständige Compiler unter modernen Softwaretools eine Seltenheit). Informationen darüber, was moderne Programmiersysteme sind und wie sie organisiert sind, finden Sie im Kapitel „Moderne Programmiersysteme“. Heutzutage sind Compiler ein integraler Bestandteil jedes Computersystems. Ohne ihre Existenz wäre die Programmierung einer Anwendungsaufgabe schwierig, wenn nicht sogar unmöglich. Und die Programmierung spezialisierter Systemaufgaben erfolgt in der Regel, wenn nicht in einer Hochsprache (in dieser Rolle wird derzeit am häufigsten die Sprache C verwendet), dann in Assemblersprache, daher kommt ein entsprechender Compiler zum Einsatz. Das direkte Programmieren in Maschinencodesprachen kommt äußerst selten vor und dient nur der Lösung sehr enger Probleme. Ein paar Worte zu Beispielimplementierungen von Compilern und Interpretern sowie zu deren Beziehung zu anderen vorhandenen Software. Compiler sind, wie später gezeigt wird, normalerweise etwas einfacher zu implementieren als Interpreter. Auch in der Effizienz sind sie überlegen – es ist offensichtlich, dass der kompilierte Code immer schneller ausgeführt wird als die Interpretation eines ähnlichen Quellprogramms. Darüber hinaus erlaubt nicht jede Programmiersprache die Konstruktion eines einfachen Interpreters. Allerdings haben Dolmetscher einen wesentlichen Vorteil: Der kompilierte Code ist immer an die Architektur des Computersystems gebunden, auf das er abzielt, und das Quellprogramm ist nur an die Semantik der Programmiersprache gebunden, die viel einfacher zu standardisieren ist. Dieser Aspekt wurde zunächst nicht berücksichtigt. Die ersten Compiler waren Mnemonik-Code-Compiler. Ihre Nachkommen – moderne Compiler aus Assemblersprachen – existieren für fast alle bekannten Computersysteme. Sie sind äußerst architektonisch orientiert. Dann erschienen Compiler aus Sprachen wie FORTRAN, ALGOL-68, PL/1. Sie waren auf große Computer mit Stapelverarbeitung von Aufgaben ausgerichtet. Von den oben genannten wird vielleicht nur FORTRAN bis heute verwendet, da es über eine große Anzahl von Bibliotheken für verschiedene Zwecke verfügt. Viele Sprachen wurden nach ihrer Entstehung nie weit verbreitet – ADA, Modula, Simula sind nur einem engen Kreis von Spezialisten bekannt. Zur gleichen Zeit auf dem Markt Softwaresysteme wird von Sprachcompilern dominiert, von denen keine große Zukunft erwartet wird. Zunächst einmal sind es jetzt C und C++. Der erste von ihnen wurde mit geboren Betriebssysteme Typ UNIX gewann zusammen mit ihm seinen „Platz an der Sonne“ und wechselte dann zu anderen Betriebssystemtypen. Die zweite verkörperte erfolgreich ein Beispiel für die Umsetzung objektorientierter Programmierideen auf bewährter praktischer Basis 1. Erwähnenswert ist auch das recht weit verbreitete Pascal, das für viele unerwartet über den Rahmen einer reinen Bildungssprache für das universitäre Umfeld hinausging.

Die Geschichte der Dolmetscher ist (noch!) nicht so reichhaltig. Wie bereits erwähnt, wurde ihnen zunächst keine große Bedeutung beigemessen, da sie den Compilern in fast allen Belangen unterlegen sind. Von den bekannten Sprachen, die einer Interpretation bedürfen, kann nur Basic erwähnt werden, obwohl die meisten Menschen mittlerweile die kompilierte Implementierung Visual Basic von Microsoft kennen. Mittlerweile hat sich die Situation jedoch etwas geändert, da mit der Entwicklung des Internets die Frage der Programmportabilität und ihrer Hardware-Plattform-Unabhängigkeit immer relevanter wird. Das bekannteste Beispiel ist heute die Java-Sprache (selbst kombiniert Kompilierung und Interpretation) und das damit verbundene JavaScript. Schließlich ist auch die HTML-Sprache, auf der das HTTP-Protokoll basiert und das den Anstoß für eine so rasante Entwicklung des World Wide Web gab, eine interpretierte Sprache. Laut dem Autor warten auf dem Gebiet der Entstehung neuer Interpreter immer noch Überraschungen auf alle, und die ersten davon sind bereits aufgetaucht – zum Beispiel die C#-Sprache („C-sharp“, aber der Name lautet überall „ C Sharp“), angekündigt von Microsoft.

UM Über die Geschichte der Programmiersprachen und den aktuellen Stand des Compiler-Marktes kann man noch lange reden. Der Autor hält es für möglich, sich auf das bereits Gesagte zu beschränken, da dies nicht der Zweck dieses Handbuchs ist. Interessierte können auf die Literatur verweisen.

Sendebühnen. Allgemeines Schema des Übersetzerbetriebs

In Abb. 13.1 zeigt das allgemeine Schema des Compilers. Daraus geht klar hervor ъ Im Allgemeinen besteht der Kompilierungsprozess aus zwei Hauptphasen – Synthese und Analyse.

In der Analysephase wird der Text des Quellprogramms erkannt und Identifikatortabellen erstellt und ausgefüllt. Das Ergebnis seiner Arbeit ist eine bestimmte interne Darstellung des Programms, die für den Compiler verständlich ist.

In der Synthesephase wird basierend auf der internen Darstellung des Programms und den in der Tabelle(n) der Bezeichner enthaltenen Informationen der Text des resultierenden Programms generiert. Das Ergebnis dieser Phase ist Objektcode.

Darüber hinaus enthält der Compiler einen für die Fehleranalyse und -korrektur zuständigen Teil, der den Benutzer bei einem Fehler im Text des Quellprogramms möglichst umfassend über die Art des Fehlers und den Ort seines Auftretens informieren soll. Bestenfalls kann der Compiler dem Benutzer eine Option zur Korrektur des Fehlers anbieten.

Diese Phasen bestehen wiederum aus kleineren Phasen, die als Kompilierungsphasen bezeichnet werden. Der Aufbau der Kompilierungsphasen ist in allgemeinster Form, deren konkrete Umsetzung und Interaktionsprozess gegeben

Erstens ist es ein Resolver für die Sprache des Quellprogramms. Dann muss er als Eingabe eine Kette von Symbolen der Eingabesprache erhalten, prüfen, ob sie zur Sprache gehört, und außerdem die Regeln identifizieren, nach denen diese Kette aufgebaut ist (da die Antwort auf die Frage nach der Zugehörigkeit „ja“ und „nein“ lautet). „ist von geringem Interesse). Es ist interessant, dass der Erzeuger der Eingabesprachketten der Benutzer ist – der Autor des Eingabeprogramms.

Zweitens ist der Compiler ein Generator für die Sprache des resultierenden Programms. Er muss am Ausgang eine Kette von Ausgabesprachen gemäß Opra konstruieren; lenäre Regeln, die beabsichtigte Maschinenanweisungssprache oder -sprache ich Sampler. Der Erkenner dieser Kette wird das Computersystem sein, für das das resultierende Programm erstellt wird.

Lexikalische Analyse(Scanner) ist der Teil des Compilers, der das Programm in der Quellsprache liest und daraus Wörter (Tokens) der Quellsprache konstruiert. Die Eingabe des lexikalischen Analysators ist der Text des Quellprogramms, und die Ausgabeinformationen werden in der Analysephase zur weiteren Verarbeitung an den Compiler übertragen. Aus theoretischer Sicht ist ein lexikalischer Analysator kein zwingender, notwendiger Teil des Compilers. Es gibt jedoch Gründe, warum es in fast allen Compilern vorhanden ist. Weitere Einzelheiten finden Sie im Abschnitt „Lexikalische Analysatoren“. Prinzipien zur Konstruktion von Scannern.“

Parsing– Dies ist der Hauptteil des Compilers in der Analysephase. O führt die Auswahl syntaktischer Strukturen im Text des Quellprogramms durch, der vom lexikalischen Analysator verarbeitet wird. Gleichzeitig prüft der Compiler die syntaktische Korrektheit des Programms. Der syntaktische Parser spielt die Hauptrolle – die Rolle eines Texterkenners der Eingabeprogrammiersprache (siehe Abschnitt „Parser. Syntaktisch kontrollierte Übersetzung“ dieses Kapitels).

Semantische Analyse- Dies ist der Teil des Compilers, der den korrekten* Text des Quellprogramms im Hinblick auf die Semantik der Eingabesprache überprüft. KRS prüft direkt, die semantische Analyse muss eine Konvertierung durchführen; Textnamen, die aufgrund der Semantik der Eingabesprache erforderlich sind (z. B. das Hinzufügen impliziter Typkonvertierungsfunktionen). In verschiedenen Implementierungen von comp. Außerdem kann die semantische Analyse teilweise in die syntaktische* Parsing-Phase und teilweise in die Vorbereitungsphase für die Codegenerierung einbezogen werden.

Vorbereitung zum Generieren von Code- Dies ist die Phase, in der der Compiler vorbereitende Aktionen durchführt, die in direktem Zusammenhang mit der Synthese des resultierenden Programmtextes stehen, aber noch nicht zur Textgenerierung in einer Fremdsprache führen. Typischerweise umfasst diese Phase Aktionen im Zusammenhang mit der Identifizierung von Sprachelementen, der Speicherzuweisung usw. (siehe Abschnitt „Semantische Analyse und Vorbereitung für die Codegenerierung“, Kapitel 14).

Codegenerierung- Dies ist eine Phase, die direkt mit der Bildung eines Komas von Satzbestandteilen der Zielsprache und dem resultierenden Text als Ganzes zusammenhängt

Kann natürlich je nach Compiler-Version variieren. Allerdings sind alle vorgestellten Phasen in der einen oder anderen Form fast immer in jedem spezifischen Compiler vorhanden.

Der Compiler als Ganzes erscheint aus Sicht der Theorie formaler Sprachen in „zwei Formen“ und erfüllt zwei Hauptfunktionen. Programme. Dies ist die Hauptphase in der Synthesephase des resultierenden Programms. Neben der direkten Generierung des Textes des resultierenden Programms umfasst die Generierung in der Regel auch die Optimierung – einen Prozess, der mit der Verarbeitung bereits generierten Textes verbunden ist. Manchmal wird die Optimierung in eine separate Kompilierungsphase aufgeteilt, da sie einen erheblichen Einfluss auf die Qualität und Effizienz des resultierenden Programms hat (siehe Abschnitte „Codegenerierung. Codegenerierungsmethoden“ und „Codeoptimierung. Grundlegende Optimierungsmethoden“, Kapitel 14).

Identifikatortabellen(manchmal auch „Symboltabellen“ genannt) sind speziell organisierte Datensätze, die dazu dienen, Informationen über die Elemente des Quellprogramms zu speichern, die dann zur Generierung des Textes des resultierenden Programms verwendet werden. In einer bestimmten Compiler-Implementierung kann es eine Bezeichnertabelle oder mehrere solcher Tabellen geben. Die Elemente des Quellprogramms, deren Informationen während des Kompilierungsprozesses gespeichert werden müssen, sind Variablen, Konstanten, Funktionen usw. – die konkrete Zusammensetzung des Elementsatzes hängt von der verwendeten Eingabeprogrammiersprache ab. Der Begriff „Tabelle“ impliziert keineswegs, dass dieser Datenspeicher genau in Form von Tabellen oder anderen Informationsfeldern organisiert werden sollte – mögliche Methoden zu deren Organisation werden im Abschnitt „Identifikatortabellen“ ausführlicher erörtert. Organisation von Identifikatortabellen.“

In Abb. dargestellt. 13.1 dient die Einteilung des Kompilierungsprozesses in Phasen eher methodischen Zwecken und wird in der Praxis möglicherweise nicht so strikt eingehalten. In den weiteren Unterabschnitten dieses Handbuchs werden verschiedene Möglichkeiten zur technischen Gestaltung der vorgestellten Kompilierungsphasen betrachtet. Es wird auch aufgezeigt, wie sie zueinander in Beziehung stehen können. Hier werden wir nur die allgemeinen Aspekte dieser Art von Beziehung betrachten.

Zunächst werden in der lexikalischen Analysephase Lexeme aus dem eingegebenen Programmtext extrahiert, soweit sie für die nächste Parsingphase erforderlich sind. Zweitens können, wie weiter unten gezeigt wird, Parsing und Codegenerierung gleichzeitig durchgeführt werden. Somit können diese drei Kompilierungsphasen kombiniert arbeiten und gleichzeitig auch die Vorbereitung für die Codegenerierung durchgeführt werden. Als nächstes betrachten wir die technischen Aspekte der Implementierung der Hauptkompilierungsphasen, die eng mit dem Konzept verbunden sind Passage.

Passage-Konzept. Multi-Pass- und Single-Pass-Compiler

Wie bereits erwähnt, besteht der Prozess der Programmerstellung aus mehreren Phasen. Bei echten Compilern kann die Zusammensetzung dieser Phasen geringfügig von der oben besprochenen abweichen – einige davon können in Komponenten unterteilt werden, andere hingegen werden zu einer Phase zusammengefasst. Auch die Reihenfolge, in der die Kompilierungsphasen ausgeführt werden, kann zwischen verschiedenen Compilervarianten variieren. In einem Fall schaut sich der Compiler den Text des Quellprogramms an, führt sofort alle Kompilierungsphasen durch und erhält das Ergebnis – den Objektcode. In einer anderen Variante führt es nur einige Kompilierungsphasen am Quelltext durch und erhält nicht das Endergebnis, sondern einen Satz einiger Zwischendaten. Diese Daten werden dann erneut verarbeitet, ein Vorgang, der mehrmals wiederholt werden kann.

Echte Compiler führen die Übersetzung des Quelltextes des Programms in der Regel in mehreren Durchgängen durch.

Passage - Hierbei handelt es sich um den Prozess des sequentiellen Lesens von Daten aus dem externen Speicher durch den Compiler, deren Verarbeitung und dem Ablegen des Arbeitsergebnisses im externen Speicher. In den meisten Fällen umfasst ein einzelner Durchgang die Ausführung einer oder mehrerer Kompilierungsphasen. Das Ergebnis der Zwischendurchläufe ist die interne Darstellung des Quellprogramms, das Ergebnis des letzten Durchlaufs ist das resultierende Objektprogramm.

Als Externer Speicher Es können alle Speichermedien verwendet werden – Computer-RAM, Magnetplattenlaufwerke, Magnetbänder usw. Moderne Compiler streben in der Regel danach, den RAM des Computers maximal für die Datenspeicherung zu nutzen, und zwar nur dann, wenn nicht genügend Speicher vorhanden ist. Bei Festplatten handelt es sich um gebrauchte Festplatten. Andere Speichermedien werden in modernen Compilern aufgrund der geringen Datenaustauschgeschwindigkeit nicht verwendet.

Während jeder Durchlauf ausgeführt wird, hat der Compiler Zugriff auf die Informationen, die er aus allen vorherigen Durchläufen erhalten hat. In der Regel nutzt es in erster Linie nur Informationen aus dem unmittelbar vorangegangenen Durchlauf, kann aber prinzipiell auf Daten früherer Durchläufe bis zurück zum Quellcode des Programms zurückgreifen. Die vom Compiler bei der Ausführung von Durchgängen erhaltenen Informationen stehen dem Benutzer nicht zur Verfügung. Es wird entweder in gespeichert Arbeitsspeicher, die vom Compiler freigegeben wird, nachdem der Übersetzungsprozess abgeschlossen ist, oder als temporäre Dateien auf der Festplatte formatiert werden, die ebenfalls zerstört werden, nachdem der Compiler seine Arbeit abgeschlossen hat. Daher weiß die Person, die mit dem Compiler arbeitet, möglicherweise nicht einmal, wie viele Durchgänge der Compiler durchführt – sie sieht immer nur den Text des Quellprogramms und des resultierenden Objektprogramms. Aber die Anzahl der durchgeführten Durchgänge ist wichtig technische Spezifikationen Compiler, seriöse Unternehmen – Compiler-Entwickler geben dies normalerweise in der Beschreibung ihres Produkts an.

Es ist klar, dass Entwickler bestrebt sind, die Anzahl der von Compilern durchgeführten Durchgänge zu minimieren. Dies erhöht die Geschwindigkeit des Compilers und reduziert den Speicherbedarf. Ideal ist ein One-Pass-Compiler, der ein Quellprogramm als Eingabe verwendet und sofort ein resultierendes Objektprogramm erzeugt.

Es ist jedoch nicht immer möglich, die Anzahl der Durchgänge zu reduzieren. Die Anzahl der erforderlichen Durchgänge wird in erster Linie durch die grammatikalischen und semantischen Regeln der Ausgangssprache bestimmt. Je komplexer die Sprachgrammatik ist und je mehr Optionen die semantischen Regeln vorschlagen, desto mehr Durchläufe führt der Compiler durch (natürlich spielt auch die Qualifikation der Compiler-Entwickler eine Rolle). Aus diesem Grund arbeiten beispielsweise Compiler aus der Sprache Pascal normalerweise schneller als Compiler aus der Sprache C – die Grammatik von Pascal ist einfacher und die semantischen Regeln strenger. One-Pass-Compiler sind selten und nur für sehr einfache Sprachen möglich. Echte Compiler führen normalerweise zwei bis fünf Durchgänge durch. Daher sind echte Compiler Multi-Pass-Compiler. Am gebräuchlichsten sind beispielsweise Compiler mit zwei und drei Durchgängen: Der erste Durchgang ist die lexikalische Analyse, der zweite das Parsen und die semantische Analyse, der dritte die Codegenerierung und -optimierung (Implementierungsoptionen hängen natürlich vom Entwickler ab). In modernen Programmiersystemen wird der erste Durchgang des Compilers (lexikalische Analyse des Codes) oft parallel zur Bearbeitung des Codes des Quellprogramms durchgeführt (diese Option zum Erstellen von Compilern wird später in diesem Kapitel besprochen).

Dolmetscher. Merkmale der Konstruktion von Dolmetschern

Dolmetscher ist ein Programm, das ein Eingabeprogramm in der Quellsprache akzeptiert und ausführt. Wie oben erwähnt besteht der Hauptunterschied zwischen Interpretern und Übersetzern und Compilern darin, dass der Interpreter nicht das resultierende Programm generiert, sondern lediglich das ursprüngliche Programm ausführt.

Der Begriff „Dolmetscher“ bedeutet wie „Übersetzer“ „Übersetzer“. Aus terminologischer Sicht sind diese Konzepte ähnlich, aus Sicht der Theorie der formalen Sprachen und der Kompilierung besteht jedoch ein großer grundlegender Unterschied zwischen ihnen. Wenn die Begriffe „Übersetzer“ und „Compiler“ kaum zu unterscheiden sind, können sie nicht mit dem Begriff „Interpreter“ verwechselt werden.

Der einfachste Weg, einen Interpreter zu implementieren, besteht darin, das Quellprogramm zunächst vollständig in Maschinenanweisungen zu übersetzen und dann sofort auszuführen. In einer solchen Implementierung würde sich der Interpreter tatsächlich kaum vom Compiler unterscheiden, mit dem einzigen Unterschied, dass das resultierende Programm dem Benutzer nicht zur Verfügung stehen würde. Der Nachteil eines solchen Interpreters wäre, dass der Benutzer warten müsste, bis das gesamte Quellprogramm kompiliert ist, bevor mit der Ausführung begonnen werden kann. Tatsächlich würde ein solcher Interpreter keinen besonderen Sinn ergeben – er würde keine Vorteile gegenüber einem ähnlichen Compiler bieten 1 . Daher agiert die überwiegende Mehrheit der Interpreter so, dass sie das Quellprogramm nacheinander ausführen, sobald es am Eingang des Interpreters ankommt. Dann muss der Benutzer nicht warten, bis das gesamte Quellprogramm kompiliert ist. Darüber hinaus kann er das ursprüngliche Programm nacheinander eingeben und das Ergebnis seiner Ausführung sofort beobachten, wenn Befehle eingegeben werden.

Bei dieser Arbeitsreihenfolge des Interpreters tritt ein wesentliches Merkmal auf, das ihn vom Compiler unterscheidet: Wenn der Interpreter Befehle sofort ausführt, wenn sie eintreffen, kann er keine Optimierung des Quellprogramms durchführen. Folglich wird es keine Optimierungsphase in der Gesamtstruktur des Interpreters geben. Ansonsten unterscheidet es sich kaum von der Struktur eines ähnlichen Compilers. Sie sollten lediglich berücksichtigen, dass in der letzten Phase – der Codegenerierung – Maschinenbefehle nicht in die Objektdatei geschrieben, sondern bei ihrer Generierung ausgeführt werden.

Das Fehlen eines Optimierungsschritts bestimmt ein weiteres Merkmal vieler Interpreter: Sie verwenden sehr häufig die umgekehrte polnische Notation als interne Darstellung des Programms (siehe Abschnitt „Codegenerierung. Codegenerierungsmethoden“, Kapitel 14). Diese praktische Form der Darstellung von Operationen hat nur einen wesentlichen Nachteil: Sie ist schwer zu optimieren. Aber genau das ist bei Dolmetschern nicht erforderlich.

Nicht alle Programmiersprachen ermöglichen die Konstruktion von Interpretern, die das Quellprogramm beim Empfang von Befehlen ausführen können. Dazu muss die Sprache die Existenz eines Compilers zulassen, der das Quellprogramm in einem Durchgang analysiert. Darüber hinaus kann eine Sprache beim Empfang von Befehlen nicht interpretiert werden, wenn Aufrufe von Funktionen und Datenstrukturen vor ihrer direkten Beschreibung erscheinen. Daher können Sprachen wie C und Pascal mit dieser Methode nicht interpretiert werden.

Das Fehlen eines Optimierungsschritts führt dazu, dass die Programmausführung mit einem Interpreter weniger effizient ist als mit einem ähnlichen Compiler. Darüber hinaus muss bei der Interpretation das Quellprogramm bei jeder Ausführung erneut geparst werden, während es bei der Kompilierung nur einmal geparst wird und danach immer die Objektdatei verwendet wird. Daher verlieren Interpreter gegenüber Compilern immer an Leistung.

Der Vorteil des Interpreters besteht darin, dass die Programmausführung unabhängig von der Architektur des Zielcomputersystems ist. Als Ergebnis der Kompilierung entsteht ein Objektcode, der sich immer an einer bestimmten Architektur orientiert. Um auf eine andere Architektur des Zielcomputersystems zu migrieren, muss das Programm erneut kompiliert werden. Und um ein Programm zu interpretieren, benötigen Sie lediglich seinen Quelltext und einen Dolmetscher aus der entsprechenden Sprache.

Dolmetscher waren lange Zeit deutlich seltener anzutreffen welche Schäler. In der Regel existierten Interpreter für eine begrenzte Auswahl relativ einfacher Programmiersprachen (z. B. Basic). Auf der Basis von Compilern wurden leistungsstarke professionelle Softwareentwicklungstools erstellt.

Einen neuen Impuls für die Entwicklung von Dolmetschern gab die Verbreitung von Global Computernetzwerke. Solche Netzwerke können Computer mit persönlicher Architektur umfassen, und dann wird die Anforderung einer einheitlichen Ausführung des Textes des Originalprogramms auf jedem von ihnen entscheidend. Daher entstand mit der Entwicklung globaler Netzwerke und der Verbreitung des World Wide Web das Internet
In modernen Programmiersystemen gibt es Softwareimplementierungen, die sowohl Compiler- als auch Interpreterfunktionen kombinieren – je nach Anforderung des Benutzers wird das Quellprogramm entweder kompiliert oder ausgeführt (interpretiert). Darüber hinaus umfassen einige moderne Programmiersprachen zwei Entwicklungsstufen: Zuerst wird das Quellprogramm in Zwischencode (eine Sprache auf niedriger Ebene) kompiliert und dann wird dieses Kompilierungsergebnis mit dem Interpreter dieser Zwischensprache ausgeführt. Optionen für solche Systeme werden im Kapitel „Moderne Programmiersysteme“ ausführlicher besprochen.

Ein weit verbreitetes Beispiel für eine interpretierte Sprache ist HTML (Hypertext Markup Language), eine Hypertext-Beschreibungssprache. Fast die gesamte Struktur des Internets funktioniert derzeit auf dieser Basis. Ein anderes Beispiel - Java-Sprachen und JavaScript – kombinieren Kompilierungs- und Interpretationsfunktionen. Der Text des Quellprogramms wird unabhängig von der Architektur des Zielcomputersystems in einen binären Zwischencode kompiliert; dieser Code wird über das Netzwerk verteilt und auf der Empfängerseite ausgeführt – interpretiert.

Übersetzer aus der Assemblersprache („Assembler“)

Assemblersprache - Dies ist eine Low-Level-Sprache. Die Struktur und Verbindung der Ketten dieser Sprache ähnelt den Maschinenanweisungen des Zielcomputersystems, auf dem das resultierende Programm ausgeführt werden muss. Die Verwendung der Assemblersprache ermöglicht es dem Entwickler, die Ressourcen (Prozessor, RAM, externe Geräte usw.) des Zielcomputersystems auf der Ebene von Maschinenanweisungen zu verwalten. Jede Anweisung im Assembler-Quellprogramm wird als Ergebnis der Kompilierung in eine Maschinenanweisung umgewandelt.

Ein Übersetzer aus der Assemblersprache wird natürlich immer ein Compiler sein, da die Sprache des resultierenden Programms Maschinencode ist. Ein Assembler-Übersetzer wird oft einfach als „Assembler“ oder „Assembler-Programm“ bezeichnet.

Implementierung von Compilern aus der Assemblersprache

Assemblersprachen enthalten normalerweise mnemonische Codes für Maschinenanweisungen. Am häufigsten wird die englischsprachige Befehlsmnemonik verwendet, es gibt jedoch auch andere Varianten von Assemblersprachen (einschließlich russischsprachiger Varianten). Aus diesem Grund wurde die Assemblersprache früher „mnemonische Codesprache“ genannt (heute wird dieser Name praktisch nicht mehr verwendet). Alle möglichen Befehle in jeder Assemblersprache lassen sich in zwei Gruppen einteilen: Die erste Gruppe umfasst gewöhnliche Sprachbefehle, die während des Übersetzungsprozesses in Maschinenbefehle umgewandelt werden; Die zweite Gruppe besteht aus speziellen Sprachbefehlen, die nicht in Maschinenbefehle umgewandelt werden, sondern vom Compiler zur Ausführung von Kompilierungsaufgaben (wie beispielsweise der Speicherzuweisungsaufgabe) verwendet werden. Die Syntax der Sprache ist äußerst einfach. Die Befehle des Quellprogramms sind normalerweise so geschrieben, dass sich in einer Zeile des Programms ein Befehl befindet. Jede Assembler-Anweisung kann in der Regel in drei nacheinander folgende Komponenten unterteilt werden: Beschriftungen, Operationscode und Operandenfeld. Der herkömmliche Assembler-Compiler sieht außerdem die Möglichkeit vor, Kommentare im Eingabeprogramm zu haben, die durch ein bestimmtes Trennzeichen von den Befehlen getrennt sind.

Das Beschriftungsfeld enthält einen Bezeichner, der die Beschriftung darstellt, oder ist leer. Jeder Labelbezeichner kann in einem Assemblerprogramm nur einmal vorkommen. Eine Marke gilt dort als beschrieben, wo sie ist bei mittelmäßig im Programm angetroffen (vorläufige Beschreibung der Etiketten ist erforderlich). Mithilfe einer Bezeichnung kann die Kontrolle an den von ihr ausgegebenen Befehl übertragen werden. Oftmals wird eine Bezeichnung vom Rest des Befehls durch ein zyklisches Trennzeichen (meistens ein Doppelpunkt :) getrennt.

Der Operationscode ist immer eine streng definierte Mnemonik eines der möglichen Prozessorbefehle oder auch ein streng definierter Befehl (meines Compilers). Der Operationscode ist in alphabetischen Symbolen der Sprachsprache geschrieben. Meistens beträgt seine Länge 3-4, seltener - 5 oder 6 Zeichen.

Das Operandenfeld ist entweder leer oder eine Liste mit einem, zwei oder seltener drei Operanden. Die Anzahl der Operanden ist streng definiert und hängt vom Operationscode ab – jede Assembler-Operation stellt eine genau definierte Anzahl ihrer Operanden bereit. Dementsprechend entspricht jede dieser Optionen adresslosen, Uniadress-, Zwei-Adress- oder Drei-Adress-Befehlen (eine größere Anzahl von Operanden wird praktisch nicht verwendet; in modernen Computern sind sogar Drei-Adress-Befehle selten). Als Opern; Sie können Bezeichner oder Konstanten sein.

Ein Merkmal der Assemblersprache besteht darin, dass eine Reihe von Bezeichnern in n speziell zur Bezeichnung von Prozessorregistern zugewiesen werden. Solche Fikatoren bedürfen einerseits keiner Vorbeschreibung, können aber mit D1 vom Anwender nicht für andere Zwecke genutzt werden. Für jede Assemblersprache ist ein Satz dieser Bezeichner vordefiniert.

Manchmal erlaubt die Assemblersprache die Verwendung bestimmter begrenzter Kombinationen von Registerbezeichnungen, Bezeichnern und Konstanten als Operanden, die durch bestimmte Operatorzeichen kombiniert werden. Solche Ausdrücke werden am häufigsten zur Bezeichnung von Adressierungsarten verwendet, beispielsweise in Maschinenanweisungen des Zielcomputersystems.

Zum Beispiel die folgende Befehlssequenz

Dies ist ein Beispiel für eine Folge von Assembler-Befehlen; Prozessoren der Intel 80x86-Familie. Es gibt einen Datensatzbeschreibungsbefehl (db), eine Bezeichnung (Schleifen), Operationscodes (mov, dec und jnz). Die Operanden sind die Datensatzkennung (datas) und Prozessregisterbezeichnungen

(bx und cx), Label (Schleifen) und Konstante (4). Die zusammengesetzten Operandendaten bilden die indirekte Adressierung des Datensatzes auf das Basisregister bx bei Offset 4 ab.

Eine solche Sprachsyntax lässt sich leicht mit einer regulären Grammatik beschreiben. Daher ist der Aufbau einer Erkennungsfunktion für Assembler nicht schwierig. Aus dem gleichen Grund werden in Assembler-Compilern normalerweise lexikalische und syntaktische Analyse in einem Erkenner kombiniert.

Die Semantik der Assemblersprache wird vollständig durch das Zielcomputersystem bestimmt, auf das diese Sprache ausgerichtet ist. Die Semantik der Assemblersprache bestimmt, welche Maschinenanweisung jeder Assembleranweisung entspricht und welche Operanden und wie viele für einen bestimmten Opcode zulässig sind.

Daher ist die semantische Analyse in einem Assembler-Compiler genauso einfach wie die syntaktische Analyse. Seine Hauptaufgabe besteht darin, die Gültigkeit der Operanden für jeden Opcode zu überprüfen und außerdem zu überprüfen, ob alle im Eingabeprogramm vorkommenden Bezeichner und Labels beschrieben sind und die sie bezeichnenden Bezeichner nicht mit den vordefinierten Bezeichnern übereinstimmen, die zur Bezeichnung von Opcodes und Prozessorregistern verwendet werden.

Die Parsing- und semantischen Analyseschemata im Assembler-Compiler können somit auf der Basis einer herkömmlichen Finite-State-Maschine implementiert werden. Es war diese Eigenschaft, die dazu führte, dass Assembler-Compiler historisch gesehen die ersten Compiler waren, die für Computer erstellt wurden. Darüber hinaus gibt es eine Reihe weiterer Funktionen, die spezifisch für Assemblersprachen sind und den Aufbau von Compilern für diese vereinfachen.

Erstens erfordern Assembler-Compiler keine zusätzliche Identifizierung von Variablen – alle Sprachvariablen behalten die ihnen vom Benutzer zugewiesenen Namen. Die Eindeutigkeit der Namen im Quellprogramm liegt in der Verantwortung des Entwicklers; die Semantik der Sprache stellt keine zusätzlichen Anforderungen an diesen Prozess. Zweitens ist in Assembler-Compilern die Speicherzuweisung extrem vereinfacht. Der Assembler-Compiler funktioniert nur mit statischem Speicher. Wenn dynamischer Speicher verwendet wird, müssen Sie zum Arbeiten damit die entsprechenden Bibliotheks- oder Betriebssystemfunktionen verwenden, und der Entwickler des Quellprogramms ist für die Zuweisung verantwortlich. Der Quellprogrammentwickler ist auch für die Übergabe von Parametern und die Organisation der Prozedur- und Funktionsspeicheranzeige verantwortlich. Er muss sich auch um die Trennung der Daten vom Programmcode kümmern – ein Assembler-Compiler führt im Gegensatz zu Compilern aus Hochsprachen eine solche Trennung nicht automatisch durch. Und drittens wird in der Phase der Codegenerierung im Compiler aus Assembler keine Optimierung durchgeführt, da der Entwickler des Quellprogramms selbst für die Organisation der Berechnungen, die Reihenfolge der Maschinenanweisungen und die Verteilung der Prozessorregister verantwortlich ist.

Abgesehen von diesen Funktionen ist der Assembler-Compiler ein regulärer Compiler, der jedoch im Vergleich zu jedem Hochsprachen-Compiler deutlich vereinfacht ist. Assembler-Compiler werden meist mit einem Zwei-Durchgangs-Schema implementiert. Beim ersten Durchgang analysiert der Compiler das Quellprogramm, wandelt es in Maschinencode um und füllt gleichzeitig die Bezeichnertabelle. Doch beim ersten Durchlauf der Maschinenbefehle bleiben die Adressen derjenigen Operanden, die sich im RAM befinden, leer. Beim zweiten Durchgang trägt der Compiler diese Adressen ein und erkennt gleichzeitig unbeschriebene Bezeichner. Dies liegt daran, dass der Operand nach der ersten Verwendung im Programm deklariert werden kann. Dann ist seine Adresse zum Zeitpunkt der Erstellung des Maschinenbefehls noch nicht bekannt und daher ist ein zweiter Durchgang erforderlich. Ein typisches Beispiel für einen solchen Operanden ist ein Label, das das Vorwärtsspringen in einer Befehlsfolge ermöglicht.

Makros und Makros

Das Entwickeln von Programmen in Assembler ist ein ziemlich arbeitsintensiver Prozess, der oft die einfache Wiederholung derselben Vorgänge erfordert, die immer wieder auftreten. Ein Beispiel wäre eine Folge von Befehlen, die jedes Mal ausgeführt werden, um bei der Eingabe einer Prozedur oder Funktion eine Stapelspeicheranzeige zu organisieren.

Um dem Entwickler die Arbeit zu erleichtern, wurden sogenannte Makrobefehle erstellt.

Makrobefehl ist eine Textersetzung, bei der jeder Bezeichner eines bestimmten Typs durch eine Zeichenkette aus einem Datenspeicher ersetzt wird. Der Vorgang der Ausführung eines Makrobefehls wird als Makrogenerierung bezeichnet, und die Zeichenkette, die sich aus der Ausführung eines Makrobefehls ergibt, wird als Makroerweiterung bezeichnet.

Der Prozess der Ausführung von Makros besteht darin, den Text des Quellprogramms nacheinander zu scannen, bestimmte darin enthaltene Bezeichner zu erkennen und sie durch die entsprechenden Zeichenfolgen zu ersetzen. Darüber hinaus wird eine textuelle Ersetzung einer Zeichenkette (Bezeichner) durch eine andere Zeichenkette (String) durchgeführt. Eine solche Substitution wird Makrosubstitution genannt.

Mithilfe von Makrodefinitionen wird festgelegt, welche Bezeichner durch welche Zeichenfolgen ersetzt werden sollen. Makrodefinitionen sind direkt im Text des Quellprogramms vorhanden. Sie werden durch spezielle Schlüsselwörter oder Trennzeichen hervorgehoben, die an keiner anderen Stelle im Programmtext vorkommen dürfen. Bei der Verarbeitung werden alle Makrodefinitionen vollständig aus dem Text des Eingabeprogramms ausgeschlossen und die darin enthaltenen Informationen für die Verarbeitung bei der Ausführung von Makrobefehlen gespeichert.

Eine Makrodefinition kann Parameter enthalten. Dann muss jeder entsprechende Makrobefehl beim Aufruf anstelle jedes Parameters eine Zeichenfolge enthalten. Beim Ausführen eines Makros wird diese Zeichenfolge an jeder Stelle eingefügt, an der der entsprechende Parameter in der Makrodefinition vorkommt. Der Parameter eines Makrobefehls kann ein anderer Makrobefehl sein, dann wird er rekursiv aufgerufen, wann immer eine Parameterersetzung durchgeführt werden muss. Grundsätzlich können Makroanweisungen eine Sequenz bilden

Rekursive Aufrufe ähneln der Abfolge rekursiver Aufrufe von Prozeduren und Funktionen, führen jedoch anstelle von Berechnungen und der Übergabe von Parametern nur Textersetzungen durch 1 .

Makros und Makrodefinitionen werden von einem speziellen Modul namens Makroprozessor oder Makrogenerator verarbeitet. Der Makrogenerator empfängt als Eingabe den Text des Quellprogramms, der Makrodefinitionen und Makrobefehle enthält, und seine Ausgabe erscheint als Text der Makroerweiterung des Quellprogramms, der keine Makrodefinitionen und Makrobefehle enthält. Bei beiden Texten handelt es sich ausschließlich um Programmtexte, eine weitere Verarbeitung erfolgt nicht. Dabei handelt es sich um die Makroerweiterung des Quelltextes, die in die Eingabe des Compilers gelangt.

Die Syntax von Makrobefehlen und Makrodefinitionen ist nicht streng definiert. Dies kann je nach Implementierung des Assembler-Compilers variieren. Das eigentliche Prinzip der Makroersetzungen im Programmtext bleibt jedoch unverändert und hängt nicht von deren Syntax ab.

Ein Makrogenerator existiert meist nicht als separates Softwaremodul, sondern ist im Assembler-Compiler enthalten. Die Makroerweiterung des Originalprogramms steht dem Entwickler normalerweise nicht zur Verfügung. Darüber hinaus können Makroersetzungen sequentiell durchgeführt werden, wenn der Quelltext im ersten Durchgang des Compilers zusammen mit dem gesamten Programmtext analysiert wird, und dann ist die Makroerweiterung des Quellprogramms als Ganzes als solche möglicherweise überhaupt nicht vorhanden.

Der folgende Text definiert beispielsweise das Makro push_0 in der Assemblersprache eines Intel 8086-Prozessors:

Hog ah, ah ■ Stoßaxt endm

Die Semantik dieses Makros besteht darin, die Zahl „0“ über das Prozessorregister ah auf den Stapel zu schreiben. Dann überall im Programmtext, wo die Makroanweisung erscheint

Es wird als Ergebnis der Makroersetzung durch eine Befehlsfolge ersetzt:

Hog ah, ah ■ Stoßaxt

Dies ist die einfachste Version der Makrodefinition. Es ist möglich, komplexere Makrodefinitionen mit Parametern zu erstellen. Eine solche Makrodefinition wird im Folgenden beschrieben:

Die Tiefe einer solchen Rekursion ist normalerweise sehr begrenzt. Die Reihenfolge rekursiver Aufrufe von Makrobefehlen unterliegt in der Regel deutlich strengeren Beschränkungen als die Reihenfolge rekursiver Aufrufe von Prozeduren und Funktionen, die bei einer Stapelorganisation der Speicheranzeige nur durch die Größe des Parameterübergabestapels begrenzt ist. add_abx-Makro xl,x2

Axt schieben
endm

Dann muss der Makrobefehl auch im Programmtext mit der entsprechenden Anzahl an Parametern angegeben werden. In diesem Beispiel das Makro

Add_abx4,8 wird als Ergebnis der Makroersetzung durch eine Befehlsfolge ersetzt:

Füge ah hinzu, 4 füge bx hinzu. 4 füge ex hinzu, 8 Stoßaxt

Viele Assembler-Compiler ermöglichen noch komplexere Konstrukte, die lokale Variablen und Labels enthalten können. Ein Beispiel für eine solche Konstruktion ist eine Makrodefinition:

Loop_ax-Makro xl,x2,yl

Hog bx.bx Loopax: bx.yl hinzufügen

Hier ist die Bezeichnung 1 oopax lokal und nur innerhalb dieser Makrodefinition definiert. In diesem Fall ist eine einfache Textersetzung eines Makrobefehls im Programmtext nicht mehr möglich, da eine zweimalige Ausführung dieses Makrobefehls zum Erscheinen zweier identischer Beschriftungen 1 oorax im Programmtext führt. In dieser Ausführungsform muss der Makrogenerator komplexere Textersetzungstechniken verwenden, ähnlich denen, die von Compilern zur Identifizierung der lexikalischen Elemente des Eingabeprogramms verwendet werden, um allen möglichen lokalen Variablen und Makrobezeichnungen innerhalb des gesamten Programms eindeutige Namen zu geben. Makros und Makroanweisungen haben nicht nur in Assemblersprachen, sondern auch in vielen Hochsprachen Verwendung gefunden. Dort werden sie von einem speziellen Modul namens Sprachpräprozessor verarbeitet (der C-Sprachpräprozessor ist beispielsweise weithin bekannt). Das Verarbeitungsprinzip bleibt das gleiche wie bei Assemblerprogrammen – der Präprozessor führt Textersetzungen direkt in den Zeilen des Quellprogramms selbst durch. In Hochsprachen müssen Makrodefinitionen vom Text des Quellprogramms selbst getrennt werden, damit der Präprozessor sie nicht mit syntaktischen Konstrukten der Eingabesprache verwechseln kann. Hierzu werden entweder spezielle Symbole und Befehle (Präprozessorbefehle) verwendet, die niemals im Text des Quellprogramms vorkommen dürfen, oder es kommen Makrodefinitionen vor

Innerhalb eines unbedeutenden Teils des Quellprogramms werden sie in Kommentare eingefügt (eine solche Implementierung existiert beispielsweise im von Borland erstellten Pascal-Compiler). Makrobefehle hingegen können an beliebiger Stelle im Quelltext des Programms vorkommen und ihr syntaktischer Aufruf darf sich nicht vom Aufruf von Funktionen in der Eingabesprache unterscheiden.

Es ist zu beachten, dass sich Makrobefehle trotz der Ähnlichkeit der Aufrufsyntax grundlegend von Prozeduren und Funktionen unterscheiden, da sie keinen resultierenden Code generieren, sondern Textersetzungen sind, die direkt im Text des Quellprogramms durchgeführt werden. Das Ergebnis des Aufrufs einer Funktion und eines Makros kann daher erheblich unterschiedlich sein.

Schauen wir uns ein Beispiel in C an. Wenn eine Funktion beschrieben wird

Int fKint a) ( return a + a: ) und ein ähnlicher Makrobefehl

#define f2(a) ((a) + (a)), dann ist das Ergebnis ihres Aufrufs nicht immer dasselbe.

Tatsächlich führen die Aufrufe j=fl(i) und j=f2(i) (wobei i und j einige ganzzahlige Variablen sind) zum gleichen Ergebnis. Aber die Aufrufe j=fl(++i) und j=f2(++i) ergeben unterschiedliche Bedeutungen Variable j. Tatsache ist, dass, da f2 eine Makrodefinition ist, im zweiten Fall eine Textersetzung durchgeführt wird, die zu einer Folge von Operatoren j=((++i) + (++i)) führt. Sie können sehen, dass in dieser Sequenz die ++i-Operation zweimal ausgeführt wird, im Gegensatz zum fl(++i)-Funktionsaufruf, wo sie nur einmal ausgeführt wird.

Da der Text eines in einer Programmiersprache geschriebenen Programms für einen Computer nicht verständlich ist, muss er in Maschinensprache übersetzt werden. Man nennt es die Übersetzung eines Programms aus einer Programmiersprache in eine Maschinencodesprache übertragen(Übersetzung - Übersetzung), und es wird von speziellen Programmen durchgeführt - Rundfunkveranstalter.

Es gibt zwei Arten von Übersetzern: Interpreter und Compiler.

Dolmetscher wird als Übersetzer bezeichnet, der eine Anweisung für Anweisung (Anweisung für Befehl) Übersetzung und anschließende Ausführung der übersetzten Anweisung des Quellprogramms durchführt. Zwei Nachteile der Interpretationsmethode:

1. Das Interpretationsprogramm muss sich während des gesamten Ausführungsprozesses des Originalprogramms im Computerspeicher befinden, dh eine bestimmte Menge an Speicher belegen.

2. Der Vorgang der Übersetzung desselben Operators wird so oft wiederholt, wie dieser Befehl im Programm ausgeführt werden muss.

Compiler ist ein Programm, das ein Quellprogramm in ein Programm (Modul) in Maschinensprache umwandelt (übersetzt). Anschließend wird das Programm in den Speicher des Computers geschrieben und erst dann ausgeführt.

Bei der Kompilierung werden die Übersetzungs- und Ausführungsprozesse zeitlich getrennt: Zuerst wird das Quellprogramm vollständig in Maschinensprache übersetzt (danach ist das Vorhandensein eines Übersetzers im RAM nicht erforderlich), und dann kann das übersetzte Programm viele Male ausgeführt werden .

Jeder Übersetzer löst die folgenden Hauptaufgaben:

1. Analysiert das übersetzte Programm und stellt fest, ob es Syntaxfehler enthält;

2. Erzeugt ein Ausgabeprogramm in einer Computerbefehlssprache;

3. Reserviert Speicher für das Ausgabeprogramm, d. h. Jeder Variablen, Konstanten, Arrays und anderen Objekten wird ein eigener Speicherabschnitt zugewiesen.

Auf diese Weise, Compiler(Englisch) Compiler- Compiler, Collector) liest das gesamte Programm vollständig, übersetzt es und erstellt eine vollständige Version des Programms in Maschinensprache, die dann ausgeführt wird.

Dolmetscher(Englisch) Dolmetscher- Dolmetscher, Dolmetscher) übersetzt und führt das Programm aus Zeile für Zeile.

Sobald ein Programm kompiliert ist, werden weder das Quellprogramm noch der Compiler mehr benötigt. Gleichzeitig muss das vom Interpreter verarbeitete Programm erneut ausgeführt werden überweisen jedes Mal, wenn das Programm gestartet wird, in Maschinensprache umgewandelt.

Jede spezifische Sprache ist entweder auf Kompilation oder Interpretation ausgerichtet – je nachdem, für welchen Zweck sie erstellt wurde. Zum Beispiel, Pascal Wird normalerweise zur Lösung recht komplexer Probleme verwendet, bei denen die Programmgeschwindigkeit wichtig ist. Daher wird diese Sprache normalerweise mit implementiert Compiler. Andererseits, BASIC wurde als Sprache für unerfahrene Programmierer entwickelt, für die die zeilenweise Ausführung eines Programms unbestreitbare Vorteile hat. Manchmal gibt es für eine Sprache und Compiler, und Dolmetscher. In diesem Fall können Sie einen Interpreter zum Entwickeln und Testen des Programms verwenden und anschließend das debuggte Programm kompilieren, um seine Ausführungsgeschwindigkeit zu verbessern.

Da in einer Programmiersprache geschriebene Texte für einen Computer unverständlich sind, müssen sie in Maschinencode übersetzt werden. Diese Übersetzung eines Programms aus einer Programmiersprache in eine Maschinencodesprache wird Übersetzung genannt und wird von speziellen Programmen – Übersetzern – durchgeführt.

Ein Übersetzer ist ein Dienstprogramm, das ein in der Eingabeprogrammiersprache bereitgestelltes Quellprogramm in ein in einer Objektsprache dargestelltes Arbeitsprogramm umwandelt.

Derzeit werden Übersetzer in drei Hauptgruppen eingeteilt: Assembler, Compiler und Interpreter.

Ein Assembler ist ein Systemdienstprogramm, das symbolische Strukturen in maschinensprachliche Befehle umwandelt. Eine Besonderheit von Assemblern besteht darin, dass sie eine wörtliche Übersetzung einer symbolischen Anweisung in eine Maschinenanweisung durchführen. Daher soll die Assemblersprache (auch Autocode genannt) die Wahrnehmung des Computerbefehlssystems erleichtern und die Programmierung in diesem Befehlssystem beschleunigen. Für einen Programmierer ist es viel einfacher, sich die mnemonische Bezeichnung von Maschinenanweisungen zu merken als ihren Binärcode.

Gleichzeitig enthält die Assemblersprache neben Analoga von Maschinenbefehlen viele zusätzliche Anweisungen, die insbesondere die Verwaltung von Computerressourcen, das Schreiben sich wiederholender Fragmente und die Erstellung von Programmen mit mehreren Modulen erleichtern. Daher ist die Ausdruckskraft der Sprache viel reicher als nur eine symbolische Codierungssprache, was die Programmiereffizienz erheblich verbessert.

Ein Compiler ist ein Dienstprogramm, das ein in der Quellprogrammiersprache geschriebenes Programm in Maschinensprache übersetzt. Genau wie ein Assembler konvertiert ein Compiler ein Programm von einer Sprache in eine andere (meistens in die Sprache eines bestimmten Computers). Gleichzeitig unterscheiden sich ausgangssprachliche Befehle hinsichtlich Organisation und Leistungsfähigkeit erheblich von maschinensprachlichen Befehlen. Es gibt Sprachen, in denen ein Befehl der Ausgangssprache in 7-10 Maschinenbefehle übersetzt wird. Es gibt jedoch auch Sprachen, in denen jeder Befehl 100 oder mehr Maschinenbefehle haben kann (z. B. Prolog). Darüber hinaus verwenden die Quellsprachen häufig eine strikte Datentypisierung, die durch ihre vorläufige Beschreibung erfolgt. Beim Programmieren kommt es möglicherweise nicht darauf an, einen Algorithmus zu programmieren, sondern darauf, sorgfältig über Datenstrukturen oder Klassen nachzudenken. Der Prozess der Übersetzung aus solchen Sprachen wird normalerweise als Kompilierung bezeichnet, und die Quellsprachen werden normalerweise als Programmiersprachen auf hoher Ebene (oder Hochsprachen) klassifiziert. Die Abstraktion einer Programmiersprache vom Computerbefehlssystem führte zur eigenständigen Schaffung einer Vielzahl von Sprachen, die auf die Lösung spezifischer Probleme ausgerichtet sind. Für wissenschaftliche Berechnungen, wirtschaftliche Berechnungen, den Zugriff auf Datenbanken und andere sind Sprachen erschienen.

Dolmetscher – ein Programm oder Gerät, das eine Operator-für-Operator-Übersetzung und Ausführung des Quellprogramms durchführt. Im Gegensatz zu einem Compiler erzeugt ein Interpreter kein Maschinensprachenprogramm als Ausgabe. Sobald ein Befehl in der Quellsprache erkannt wird, führt er ihn sofort aus. Sowohl Compiler als auch Interpreter verwenden dieselben Methoden zur Analyse des Quellcodes eines Programms. Mit dem Interpreter können Sie jedoch mit der Datenverarbeitung beginnen, nachdem Sie auch nur einen Befehl geschrieben haben. Dadurch wird der Prozess der Programmentwicklung und des Debuggens flexibler. Darüber hinaus ermöglicht das Fehlen von Ausgabemaschinencode, dass externe Geräte nicht mit zusätzlichen Dateien „überladen“ werden, und der Interpreter selbst kann ganz einfach an jede Maschinenarchitektur angepasst werden, da er nur einmal in einer weit verbreiteten Programmiersprache entwickelt wurde. Daher haben sich interpretierte Sprachen wie Java Script und VB Script weit verbreitet. Der Nachteil von Interpretern ist die geringe Geschwindigkeit der Programmausführung. Typischerweise laufen interpretierte Programme 50 bis 100 Mal langsamer als native Programme.

Ein Emulator ist ein Programm oder Software- und Hardware-Tool, das die Möglichkeit bietet, ohne Neuprogrammierung auf einem bestimmten Computer ein Programm auszuführen, das Codes oder Methoden zur Ausführung von Vorgängen verwendet, die sich von denen des jeweiligen Computers unterscheiden. Ein Emulator ähnelt einem Interpreter darin, dass er ein in einer bestimmten Sprache geschriebenes Programm direkt ausführt. Meistens handelt es sich jedoch um Maschinensprache oder Zwischencode. Beide stellen Anweisungen im Binärcode dar, die unmittelbar nach Erkennung des Operationscodes ausgeführt werden können. Im Gegensatz zu Textprogrammen ist es nicht erforderlich, die Programmstruktur zu kennen oder Operanden auszuwählen.

Emulatoren werden häufig für verschiedene Zwecke eingesetzt. Beispielsweise wird bei der Entwicklung neuer Computersysteme zunächst ein Emulator erstellt, der Programme ausführt, die für Computer entwickelt wurden, die es noch nicht gibt. Dadurch ist es möglich, das Befehlssystem zu evaluieren und die Basissoftware zu entwickeln, noch bevor die entsprechende Hardware erstellt wird.

Sehr oft wird ein Emulator verwendet, um alte Programme auf neuen Computern auszuführen. In der Regel sind neuere Computer schneller und verfügen über bessere Peripheriegeräte. Dadurch können Sie ältere Programme effizienter emulieren, als sie auf älteren Computern auszuführen.

Ein Transcoder ist ein Programm oder Softwaregerät, das in der Maschinensprache eines Computers geschriebene Programme in Programme in der Maschinensprache eines anderen Computers übersetzt. Wenn der Emulator ein weniger intelligentes Analogon des Interpreters ist, verhält sich der Transcoder im Verhältnis zum Compiler in gleicher Funktion. In ähnlicher Weise wird Quell- (und normalerweise binärer) Maschinencode oder eine Zwischendarstellung mit einer einzigen Anweisung und ohne allgemeine Analyse der Struktur des Quellprogramms in anderen ähnlichen Code umgewandelt. Transcoder sind nützlich, wenn Programme von einer Computerarchitektur auf eine andere übertragen werden. Sie können auch verwendet werden, um hochsprachliche Programmtexte aus vorhandenem Binärcode zu rekonstruieren.

Ein Makroprozessor ist ein Programm, das eine Zeichenfolge durch eine andere ersetzt. Dies ist eine Art Compiler. Es generiert Ausgabetext durch die Verarbeitung spezieller Einfügungen im Quelltext. Diese Einfügungen sind auf besondere Weise konzipiert und gehören zu Konstrukten einer Sprache, die Makrosprache genannt wird. Makroprozessoren werden oft als Add-ons zu Programmiersprachen eingesetzt und erhöhen die Funktionalität von Programmiersystemen. Fast jeder Assembler enthält einen Makroprozessor, der die Effizienz bei der Entwicklung von Maschinenprogrammen erhöht. Solche Programmiersysteme werden üblicherweise Makroassembler genannt.

Makroprozessoren werden auch bei Hochsprachen verwendet. Sie erhöhen die Funktionalität von Sprachen wie PL/1, C, C++. Makroprozessoren werden vor allem in C und C++ häufig eingesetzt, um das Schreiben von Programmen zu erleichtern. Makroprozessoren verbessern die Programmiereffizienz, ohne die Syntax oder Semantik der Sprache zu ändern.

Syntax ist eine Reihe von Regeln einer Sprache, die die Bildung ihrer Elemente bestimmen. Mit anderen Worten handelt es sich hierbei um eine Reihe von Regeln für die Bildung semantisch bedeutsamer Symbolfolgen in einer bestimmten Sprache. Die Syntax wird mithilfe von Regeln angegeben, die die Konzepte einer Sprache beschreiben. Beispiele für Konzepte sind: Variable, Ausdruck, Operator, Prozedur. Die Reihenfolge der Konzepte und ihre akzeptable Verwendung in Regeln bestimmen die syntaktisch korrekten Strukturen, aus denen Programme bestehen. Durch die Syntax wird die Hierarchie der Objekte definiert und nicht die Art und Weise, wie sie miteinander interagieren. Beispielsweise kann eine Anweisung nur in einer Prozedur vorkommen, ein Ausdruck in einer Anweisung, eine Variable kann aus einem Namen und optionalen Indizes bestehen usw. Die Syntax ist nicht mit solchen Phänomenen im Programm wie „Springen zu einer nicht vorhandenen Bezeichnung“ oder „Eine Variable mit dem angegebenen Namen ist nicht definiert“ verbunden. Das ist es, was die Semantik bewirkt.

Semantik – Regeln und Bedingungen, die die Beziehungen zwischen Sprachelementen und ihren semantischen Bedeutungen sowie die Interpretation der sinnvollen Bedeutung syntaktischer Konstruktionen der Sprache bestimmen. Objekte einer Programmiersprache werden nicht nur nach einer bestimmten Hierarchie im Text platziert, sondern sind zusätzlich durch andere Konzepte miteinander verbunden, die verschiedene Assoziationen bilden. Beispielsweise hat eine Variable, für die die Syntax nur in Deklarationen und einigen Anweisungen eine gültige Position definiert, einen bestimmten Typ, kann mit einer begrenzten Anzahl von Operationen verwendet werden, hat eine Adresse und eine Größe und muss vorher deklariert werden im Programm verwendet werden.

Ein Parser ist eine Compiler-Komponente, die Quellanweisungen auf Übereinstimmung mit den syntaktischen Regeln und der Semantik einer bestimmten Programmiersprache überprüft. Trotz seines Namens prüft der Analysator sowohl Syntax als auch Semantik. Es besteht aus mehreren Blöcken, von denen jeder seine eigenen Probleme löst. Darauf wird bei der Beschreibung der Struktur des Übersetzers näher eingegangen.

Jeder Übersetzer führt die folgenden Hauptaufgaben aus:

Analysiert das übersetzte Programm und stellt insbesondere fest, ob es Syntaxfehler enthält;

Erzeugt ein Ausgabeprogramm (oft als Objektprogramm bezeichnet) in Maschinenbefehlssprache;

Reserviert Speicher für ein Objektprogramm.