In einer Nussschale
Programmiersprachen bestehen aus einer Syntax, die das Programm als Zeichenfolge darstellt, und einer Semantik, die die beabsichtigte Bedeutung des Programms darstellt.
Formale Sprachen sind Syntax ohne Bedeutung. Es ist dazu gedacht, die Struktur von formal definierten Stringsets zu untersuchen, ohne diesen Strings üblicherweise eine Bedeutung beizumessen.
Reguläre Ausdrücke und andere Formalismen (z. B. kontextfreie Grammatiken) werden verwendet, um formale Sprachen zu definieren, die als syntaktischer Bestandteil der Programmierung und natürlicher Sprachen verwendet werden, dh um Sätze strukturiert darzustellen. Andere Mechanismen werden verwendet, um diese Struktur mit der Semantik der Programmiersprachen in Beziehung zu setzen.
Vieles wird hier erheblich vereinfacht, insbesondere in Bezug auf die natürliche Sprache.
Mit viel mehr Details
Um Ihre Frage zu beantworten, sollten wir von vorne beginnen. Eine Sprache im üblichen Sinne ist informell ein Mittel, um Informationen oder Ideen zu vermitteln. In einer Sprache unterscheidet man normalerweise zwischen Syntax und Semantik. Über Semantik möchten Sie sprechen / schreiben. die Informationen, die Sie vermitteln möchten. Syntax ist das Mittel, mit dem Sie es übermitteln, dh eine herkömmliche Darstellung, die zwischen Personen und nun auch zwischen Personen und Geräten oder zwischen Geräten (Computern) ausgetauscht werden kann.
In der Regel verwenden Sie das Wort dog
, um die Idee eines Hundes zu vermitteln. Das Wort dog
besteht aus drei Buchstaben oder einem ähnlichen Laut und soll eine Art Tier darstellen. Die Schlüsselidee ist, dass Kommunikation durch Repräsentation dessen erfolgt, was kommuniziert werden soll. Repräsentationsstrukturen werden üblicherweise als Syntax bezeichnet, während das, was dargestellt wird, als Semantik bezeichnet wird. Dies gilt mehr oder weniger für natürliche Sprache sowie für Programmiersprachen.
Wörter sind syntaktische Einheiten, die mehr oder weniger elementare semantische Konzepte darstellen. Diese elementaren Konzepte müssen jedoch auf verschiedene Weise zusammengesetzt werden, um eine komplexere Bedeutung zu erhalten. Wir schreiben
the dog
, um zu vermitteln, dass wir einen bestimmten Hund meinen, und the dog bites the cat
um eine komplexere Idee zu vermitteln. Die Art und Weise, wie die Wörter organisiert sind, muss jedoch durch Regeln festgelegt werden, damit wir erkennen können, welche der beiden Gruppen sich tatsächlich beißen.
Wir haben also Regeln wie diese sentence -> subject verb complement
, die zu Sätzen passen und uns mitteilen sollen, wie die mit jedem Teil verbundenen Ideen artikuliert sind. Diese Regeln sind syntaktische Regeln, da sie uns mitteilen, wie die Darstellung unserer Botschaft zu organisieren ist. Das subject
kann selbst durch eine Regel definiert werden subject -> article noun
und so weiter.
2 x + 1 = 23x123
equation -> expression "=" expression
expression -> expression "+" expression
expression -> number
Die Struktur der Programmiersprachen ist dieselbe. Programmiersprachen sind semantisch darauf spezialisiert, durchzuführende Berechnungen auszudrücken, anstatt zu lösende Probleme, Beweise für Theoreme oder freundschaftliche Beziehungen zwischen Tieren auszudrücken. Aber das ist der Hauptunterschied.
In der Syntax verwendete Darstellungen sind normalerweise Zeichenfolgen oder Töne für gesprochene Sprachen. Die Semantik gehört normalerweise zum abstrakten Bereich oder möglicherweise zur Realität, ist aber in unseren Denkprozessen oder im Verhaltensbereich von Geräten immer noch abstrahiert. Bei der Kommunikation wird die Information / Idee in eine Syntax kodiert, die vom Empfänger gesendet und dekodiert wird. Das Ergebnis wird dann vom Empfänger wie auch immer interpretiert.
Was wir also von der Sprache sehen, ist hauptsächlich die Syntax und ihre Struktur. Das obige Beispiel ist nur eine der gebräuchlichsten Methoden, um syntaktische Zeichenfolgen und ihre strukturelle Organisation zu definieren. Da sind andere. Für eine bestimmte Sprache können einige Zeichenfolgen einer Struktur zugewiesen werden und sollen zur Sprache gehören, während andere dies nicht tun.
Gleiches gilt für Worte. Einige Buchstabenfolgen (oder Lautfolgen) sind legitime Wörter, andere nicht.
Formale Sprachen sind nur Syntax ohne Semantik. Sie definieren mit einer Reihe von Regeln, welche Sequenzen unter Verwendung der Grundelemente eines Alphabets erstellt werden können. Was die Regeln sind, kann sehr unterschiedlich sein, manchmal komplex. Formale Sprachen werden jedoch für viele mathematische Zwecke jenseits der sprachlichen Kommunikation verwendet, sei es für natürliche oder für Programmiersprachen. Der Regelsatz, der die Zeichenfolgen in einer Sprache definiert, wird als Grammatik bezeichnet. Es gibt aber auch viele andere Möglichkeiten, Sprachen zu definieren.
In der Praxis ist eine Sprache in zwei Ebenen strukturiert. Die lexikalische Ebene definiert Wörter, die aus einem Buchstaben bestehen. Die syntaktische Ebene definiert Sätze oder Programme, die aus einem Alphabet von Wörtern (oder genauer aus Wortfamilien) aufgebaut sind, so dass es ein endliches Alphabet bleibt. Dies ist notwendigerweise etwas vereinfacht.
Die Struktur von Wörtern ist in den meisten Sprachen (Programmierung oder natürlich) ziemlich einfach, so dass sie normalerweise mit der einfachsten Formsprache definiert werden: den regulären Sprachen. Sie können mit regulären Ausdrücken (regexp) definiert werden und sind mit programmierten Geräten, so genannten Finite-State-Automaten, relativ einfach zu identifizieren. Bei Programmiersprachen sind Beispiele für ein Wort ein Bezeichner, eine Ganzzahl, eine Zeichenfolge, eine reelle Zahl, ein reserviertes Wort wie if
oder repeat
, ein Interpunktionssymbol oder eine offene Klammer. Beispiele für Wortfamilien sind Bezeichner, Zeichenfolge und Ganzzahl.
Die syntaktische Ebene wird normalerweise durch einen etwas komplexeren Typ formaler Sprache definiert: die kontextfreien Sprachen, bei denen die Wörter als Alphabet verwendet werden. Die Regeln, die wir oben gesehen haben, sind kontextfreie Regeln für die natürliche Sprache. Bei Programmiersprachen können Regeln sein:
statement -> assignment
statement -> loop
loop -> "while" expression "do" statement
assignment -> "identifier" "=" expression
expression -> "identifier"
expression -> "integer"
expression -> expression "operator" expression
Mit solchen Regeln können Sie schreiben:
while aaa /= bbb do aaa = aaa + bbb / 6
Das ist eine Aussage.
Die Art und Weise, wie es erstellt wurde, kann durch eine Baumstruktur dargestellt werden, die als Analysebaum oder Syntaxbaum bezeichnet wird (hier nicht vollständig):
statement
|
_______________ loop _______________
/ / \ \
"while" expression "do" statement
__________|_________ |
/ | \ assignment
expression "operator" expression _______|_______
| | | / | \
"identifier" "/=" "identifier" "identifier" "=" expression
| | | |
aaa bbb aaa ... ...
Die auf der linken Seite einer Regel erscheinenden Namen werden als Nichtterminals bezeichnet, während die Wörter auch als Terminals bezeichnet werden, da sie im Alphabet für die Sprache (über der lexikalischen Ebene) stehen. Nicht-Terminal stellen die verschiedenen syntaktischen Strukturen dar, die zum Erstellen eines Programms verwendet werden können.
Solche Regeln werden als kontextfrei bezeichnet, da ein Nicht-Terminal unabhängig vom Kontext, in dem es vorkommt, beliebig durch eine der entsprechenden Regeln ersetzt werden kann. Die Regeln, die die Sprache definieren, werden als kontextfreie Grammatik bezeichnet.
Tatsächlich gibt es Einschränkungen, wenn Bezeichner zuerst deklariert werden müssen oder wenn ein Ausdruck Typeinschränkungen erfüllen muss. Eine solche Einschränkung kann jedoch als semantisch und nicht als syntaktisch angesehen werden. Tatsächlich ordnen einige Fachleute sie in die sogenannte
statische Semantik ein .
Wenn ein Satz oder ein Programm gegeben ist, wird die Bedeutung dieses Satzes extrahiert, indem die vom Analysebaum für diesen Satz gegebene Struktur analysiert wird. Daher ist es sehr wichtig, Algorithmen, sogenannte Parser, zu entwickeln, die die einem Programm entsprechende Baumstruktur wiederherstellen können, wenn sie dem Programm gegeben werden.
Dem Parser geht der lexikalische Analysator voraus, der Wörter erkennt und die Familie bestimmt, zu der sie gehören. Dann wird die Folge von Wörtern oder lexikalischen Elementen an den Parser übergeben, der die zugrunde liegende Baumstruktur abruft. Aus dieser Struktur kann der Compiler dann bestimmen, wie Code generiert werden soll, was der semantische Teil der Programmverarbeitung auf der Compilerseite ist.
Der Parser eines Compilers kann tatsächlich eine dem Analysebaum entsprechende Datenstruktur erstellen und an die späteren Phasen des Kompilierungsprozesses übergeben, muss dies aber nicht. Das Ausführen des Parsing-Algorithmus läuft darauf hinaus, eine Berechnungsstrategie zu entwickeln, um den im Programmtext enthaltenen Syntaxbaum zu untersuchen. Dieser Syntax- / Analysebaum kann dabei je nach Kompilierungsstrategie (Anzahl der Stufen) erläutert werden oder auch nicht. Was jedoch notwendig ist, ist, dass es letztendlich mindestens eine Bottom-up-Untersuchung des Analysebaums gibt, ob explizit oder implizit in der Berechnungsstruktur belassen.
Der Grund dafür ist intuitiv, dass eine standardmäßige formale Methode zur Definition der einer syntaktischen Baumstruktur zugeordneten Semantik ein sogenannter Homomorphismus ist. Fürchte dich nicht vor dem großen Wort. Die Idee ist nur zu berücksichtigen, dass die Bedeutung des Ganzen sich aus der Bedeutung der Teile auf der Grundlage des Operators zusammensetzt, der sie verbindet
Zum Beispiel kann der Satz the dog bites the cat
mit der Regel analysiert werden sentence -> subject verb complement
. Die Bedeutung der drei Teilbäume zu wissen subject
, verb
und complement
die Regel , dass sie komponiert uns sagt , dass das Thema der Aktion tut, und dass die Katze ist derjenige, der gebissen wird.
Dies ist nur eine intuitive Erklärung, die jedoch formalisiert werden kann. Die Semantik wird aus den Bestandteilen aufwärts konstruiert. Das verbirgt aber viel Komplexität.
Die interne Arbeitsweise eines Compilers kann in mehrere Stufen unterteilt werden. Der eigentliche Compiler arbeitet möglicherweise stufenweise mit Zwischendarstellungen. Es kann auch einige Stufen zusammenführen. Dies hängt von der verwendeten Technologie und der Komplexität der Kompilierung der jeweiligen Sprache ab.