Was genau ist eine Programmiersprache? Was ermöglicht es uns, in einer solchen Sprache zu schreiben?

26

Okay, ich bin neu in der Programmierung und ich gebe zu, dass dies eine ziemlich abstrakte Frage ist.

Die natürliche Sprache, die wir jeden Tag sprechen, existiert, weil die Menschen sich verstehen können. Wie können Computer meinen in einer bestimmten Sprache geschriebenen Code verstehen?

Angenommen, Herr A schafft eine neue Sprache. Wie wird das von Maschinen akzeptiert? Muss der Ersteller mit der Maschine in Maschinensprache kommunizieren, um eine neue Sprache zu erstellen? Was garantiert, dass wir in einer Sprache schreiben können, während wir von der Maschine richtig verstanden werden?

Erica Xu
quelle
1
Was ermöglicht es uns, in einer solchen Sprache zu schreiben? - "Brains: der neue Wunderkopffüller!" - Spike Milligan.
Stephen C
6
Ein bisschen breit, aber dennoch eine gute Frage. Zu viele Leute benutzen einfach Sprachen, ohne sich jemals zu fragen, wie sie funktionieren. Gut, dass du neugierig bist.
Riwalk
4
Dies ist eine allgemeine Referenzfrage , die von Wikipedia einfach und trivial beantwortet wird .
Aaronaught

Antworten:

39

Sie können so ziemlich die gesamte Antwort auf Ihre Fragen mit dem Wort "Compiler" zusammenfassen . Ein Compiler ist ein spezielles Programm, dessen Aufgabe es ist, Quellcode als Eingabe zu verwenden, vom Sprachdesigner festgelegte Sprachregeln anzuwenden, um herauszufinden, was der Code bedeutet, und Code mit derselben Bedeutung in einer anderen Sprache als Ausgabe zu erzeugen. Dies ist im Allgemeinen Maschinencode oder eine Form von Bytecode (der "Maschinencode" für virtuelle Maschinen), obwohl spezialisierte Compiler existieren, die Code in andere Hochsprachen übersetzen. Sie sprengen jedoch den Rahmen dieser Frage.

Nicht alle Sprachen haben einen Compiler. Einige von ihnen haben stattdessen einen Interpreter , der die gleichen Aktionen ausführt, die ein Compiler ausführt, mit der Ausnahme, dass er, anstatt Maschinencode nach der Bestimmung der Bedeutung des Programms zu erstellen, das Programm einfach sofort ausführt. Die Grundprinzipien für das Parsen (Lesen) des Codes und das Bestimmen der Bedeutung sind jedoch dieselben.

Eine tiefere Antwort als diese würde sich mit der Compilertheorie befassen, die ein sehr breites Thema ist. Wenn Sie sich für das Thema interessieren, sollten Sie zunächst den Wikipedia-Artikel für "Compiler" lesen und die darin enthaltenen Links überprüfen. Wenn Sie spezielle Fragen haben, können Sie diese hier stellen.

Mason Wheeler
quelle
11
+1 - Ich möchte auch hinzufügen, dass Sie beim Schreiben einer neuen Sprache den Compiler oder Interpreter in einer anderen Sprache schreiben müssen. Spätere Versionen des Compilers oder Interpreters können dann in früheren Versionen der Sprache geschrieben und mit dem älteren Compiler kompiliert werden. Der allererste Assembler wurde in Maschinencode geschrieben. Der erste C-Compiler wurde (höchstwahrscheinlich) in Assembly usw. geschrieben
Scott Whitlock
1
Ich würde die Definition des Compilers ändern. Sie geben nicht alle Maschinencode aus. Besonders heutzutage mit so vielen Compilern, die "Intermediate Code" wie MSIL ausgeben. Es gibt sogar Compiler, die JavaScript ausgeben!
Neil N
3
Ich würde zögern, zu behaupten, dass Compiler Maschinencode per Definition produzieren, selbst wenn man es einem Anfänger erklärt. Das ist, als würde man sagen, dass Funktionen reelle Zahlen zurückgeben, eine sinnlose Vereinfachung. All of Compiler Konstruktion hält , wenn der Code erzeugen , die eigentlich nicht für einen Computer aus Silizium aufgebaut ist , aber nur abstrakt (sei es eine VM oder eine höhere Programmiersprache definiert ist , gibt es einen Grund den C - Standard eine definiert gesagt wird es abstrakte Maschine , und ist ein Compiler vom sehr niedrigen LLVM-IR bis zum verdammten JavaScript. Anfänger müssen das bekommen, je früher desto besser.
2
Die Vereinfachung, die die meisten Compiler-Bücher verwenden, besteht darin, dass ein Compiler Sprachregeln anwendet, um als Ausgabe eine Quellsprache in eine Zielsprache zu konvertieren. (Es ist nicht ungewöhnlich, zum Beispiel nach C zu kompilieren, insbesondere für einen Einführungskurs).
JasonTrue
4
@delnan, noch mehr - jede Sprache ist ein Maschinencode für eine eigene abstrakte Maschine. Egal wie gut die Sprache ist.
SK-logic
11

Wie Sie betonten, kommunizieren Menschen über eine "natürliche" Sprache wie Englisch, Französisch, Deutsch miteinander. Sie werden natürlich genannt, weil wir sie auf natürliche Weise erwerben, anstatt sie absichtlich zu erfinden (Esperanto ist eine Ausnahme).

Eine formale Sprache ist eine, die für den einen oder anderen Zweck erfunden wurde. Eine Programmiersprache wie beispielsweise C ist eine formale Sprache, die zum Programmieren von Computern erfunden wurde.

Alle Sprachen können mit einer Grammatik beschrieben werden. Eine Hierarchie von Grammatiken wurde 1956 von Noam Chomsky beschrieben. Sie besteht aus den folgenden Ebenen:

Typ-0-Grammatiken (uneingeschränkte Grammatiken). Sie sind die allgemeinsten und entsprechen einer Turing-Maschine. Daher ist das Problem der Entscheidung, ob eine bestimmte Zeichenfolge Teil einer uneingeschränkten Grammatik ist, nicht zu entscheiden.

Typ-1-Grammatiken (kontextsensitive Grammatiken). Fast alle natürlichen Sprachen wie Englisch sind kontextsensitiv. Ein Beispiel für Kontextsensitivität im Englischen sind die beiden Ausdrücke: "Die Zeit vergeht wie ein Pfeil." und "Obst fliegt wie eine Banane." Im Allgemeinen ist es für Computer schwierig, kontextsensitive Sprachen zu verstehen.

Typ-2-Grammatiken (kontextfrei). Kontextfreie Sprachen sind die theoretische Grundlage für die Syntax der meisten Programmiersprachen.

Typ-3-Grammatiken (reguläre Grammatiken). Die Familie der regulären Sprachen kann durch reguläre Ausdrücke erhalten werden. Reguläre Sprachen werden häufig verwendet, um Suchmuster und die lexikalische Struktur von Programmiersprachen zu definieren.

Typ 2 (kontextfrei) und Typ 3 (regulär) Grammatiken werden am häufigsten von Computern verwendet, da Parser für sie effizient implementiert werden können.

BNF (Backus-Normalform oder Backus-Naur-Form) ist eine Notationstechnik für kontextfreie Grammatiken, die häufig zur Beschreibung der Syntax von Computersprachen verwendet wird.

Ein Bezeichner könnte beispielsweise wie folgt beschrieben werden:

<identifier> ::= <letter> { <letter> | <digit> }

Das heißt, es muss mit einem Buchstaben beginnen und kann zusätzliche Buchstaben oder Ziffern enthalten.

Früher wurde ein Buchstabe als 'a' | definiert 'b' | 'c' usw., und die Ziffer wird unter Verwendung derselben Schreibweise als '0' bis '9' definiert.

Eine "for" -Anweisung kann definiert werden als:

 <for_statement> ::=
    'for' '(' <expression> ';' <expression> ';' <expression> ')' <statement> 

Lexikalische Analysatoren und Parser (die ersten Schritte eines Compilers oder Interpreters) werden dann so konstruiert, dass sie die vom BNF für eine bestimmte Sprache beschriebene spezifische Grammatik akzeptieren. Lexikalische Analysatoren werden in der Regel verwendet, um die verschiedenen Token einer Sprache (z. B. ein Schlüsselwort, ein Bezeichner oder eine Zahl) herauszusuchen, und der Parser wird verwendet, um herauszufinden, wie die Token zusammenarbeiten, z. B. wie eine "for" -Anweisung erstellt wird .

Tcrosley
quelle
+1 großartige Zusammenfassung. Aber ich bin nicht überrascht, dass dies nicht als Antwort akzeptiert wurde. Dies ist, was ich dachte, OP fragte, aber basierend auf der Antwort, die sie wählten, schien es, als wollten sie etwas viel Höheres.
Matthew Rodatus
5

Definieren wir zunächst "Sprache" in Bezug auf das, was es ist. Die Sprache benötigt zuerst ein Vokabular (eine Liste von Wörtern, die Konzepte definieren, die die Objekte der Kommunikation sind) und dann eine Syntax (ein "Primer" oder ein Satz von Regeln, die die Struktur der Kommunikation definieren).

Auf dieser grundlegendsten Ebene unterscheidet sich C # nicht wesentlich von Englisch. Was C # zu einer "Programmiersprache" macht, ist seine Absicht und damit sein Design; Es wurde entwickelt, um in einzelne Befehle auf niedriger Ebene aufgeteilt zu werden. Aus diesem Grund ist das vordefinierte Vokabular begrenzt, die Syntax wird sehr streng durchgesetzt und die gesamte Sprache ist so konzipiert, dass sie von ihrem "Publikum" (dem Computer; genauer gesagt dem Compiler, der die Informationen verarbeiten wird) auf sehr bekannte vordefinierte Weise verwendet wird den Quellcode in eine "Zwischensprache" einfacher Befehle, die dann von der "Laufzeit" weiter in Maschinencode übersetzt werden können). Sie schreiben keine Prosa oder Gedichte in C #; Sie weisen den Computer an, einen Job auf möglichst eindeutige Weise auszuführen.

Ja, für Computer ist ein Tool erforderlich, das normalerweise als Compiler bezeichnet wird, um das, was Sie in Code schreiben, in Anweisungen zu konvertieren, die der Computer verwenden kann. Die Informatik ist, wie die meisten Technologien, ein von Natur aus iterativer, "geschichteter" Prozess. Als die Computer erfunden wurden, wurden sie durch manuelle Eingabe der binären Anweisungen programmiert. Diese Anweisungen wurden für jeden Prozessor in hexadezimale "Maschinencodes" standardisiert; Der Unterschied besteht nur darin, wie die Binärziffern zur Anzeige für den Menschen gruppiert werden. Im Assembler-Code wurden dann beim Schreiben von Programmen die Befehlsliste und einige grundlegende Bezeichner wie Registernamen durch ihre Hexadezimalcodes ersetzt. ASM kann weiterhin 1: 1 in systemeigenen Maschinencode konvertiert werden. Der Quantensprung war die dritte Generation der "imperativen" Programmierung. Dabei werden allgemein verständlichere, abstraktere Konzepte wie Variablen und Logikschleifen verwendet und mit Hilfe von Stichwörtern und Syntaxmustern in systemeigene Anweisungen zerlegt. Frühe Sprachen wie COBOL, FORTRAN, Pascal und C können immer noch von einem Menschen in eine bestimmte Maschinensprache "übersetzt" werden (normalerweise 8086 ASM). Dann kam die Revolution der objektorientierten Programmierung, bei der es sich im Grunde genommen um zusätzliche Syntaxregeln handelt, die den Code als konzeptuell in "Objekte" mit einer Kombination aus Status und Logik eingekapselt definieren. von einem Menschen in eine bestimmte Maschinensprache (in der Regel 8086 ASM). Dann kam die Revolution der objektorientierten Programmierung, bei der es sich im Grunde genommen um zusätzliche Syntaxregeln handelt, die den Code als konzeptuell in "Objekte" mit einer Kombination aus Status und Logik eingekapselt definieren. von einem Menschen in eine bestimmte Maschinensprache (in der Regel 8086 ASM). Dann kam die Revolution der objektorientierten Programmierung, bei der es sich im Grunde genommen um zusätzliche Syntaxregeln handelt, die den Code als konzeptuell in "Objekte" mit einer Kombination aus Status und Logik eingekapselt definieren.

Heutzutage befinden wir uns in der "4. Generation" von Sprachen, dh Sprachen, die geschrieben wurden, um die Kommunikation mit anderen Programmen zu definieren, anstatt direkt zur Maschine. Weitgehend definiert, umfasst dies "Markup" -Sprachen wie XML / HTML, "Scripting" -Sprachen wie JavaScript und SQL sowie die meisten "Sandbox" -Sprachen wie Java und .NET Framework (die zu einer AWL kompiliert werden, die dann von weiter interpretiert wird) eine Laufzeit, die maschinen- und plattformspezifische Details abstrahiert). Man könnte auch sagen, es umfasst den Bereich der funktionalen Programmiersprachen, die SCHWER von einer Laufzeit abhängig sind, um nicht nur maschinenspezifische Details, sondern auch betriebsspezifische Details zu abstrahieren. Diese Sprachen der 4. Generation sind für einen Menschen mehr oder weniger unmöglich, in native Maschinenanweisungen zu übersetzen. und der Punkt ist, dass es kein lohnendes Unterfangen wäre; Die Stärke dieser Sprachen ist der vielschichtige Prozess, mit dem sie einem Computer schließlich mitteilen, was auf den niedrigen Ebenen zu tun ist.

KeithS
quelle
Vielen Dank. Ich habe einen Einblick in die Geschichte der Entwicklung der Programmiersprache.
Erica Xu
2
@ KeithS: Möglicherweise möchten Sie den letzten Absatz neu formatieren, um ihn ein wenig lesbarer zu machen.
Ivan Vučica
4

Das ist eine gute Frage. Eine richtige Antwort macht gut die Hälfte der sogenannten "Informatik" aus.

Für den Anfang empfehle ich, die Denotations- und Operationssemantik zu lesen und dann dieses Buch zu lesen . Sie erhalten ein mehr oder weniger solides Verständnis dafür, was die Programmiersprache ist und wie sie formal definiert werden kann.

Wenn das oben Genannte etwas zu akademisch ist, können Sie mit Petzold, "Code" , beginnen und dann zur Semantik zurückkehren.

SK-Logik
quelle
1
Sie erwarten wirklich, dass ein 18-jähriger Neuling eine schwere Theorie liest, um diese Frage zu beantworten?
Job
2
@Job, gemäß seiner vorherigen Frage, bekommt er Dosierungen von Scheme (und vermutlich SICP) an der Universität. Sollte dann mit ein bisschen Semantik in Ordnung sein. Wie auch immer, es gibt keine richtige Antwort auf diese Frage ohne schwere Theorie.
SK-logic
+1 für die Erwähnung von "Code". Dieses Buch sollte für jeden CS-Einsteiger gelesen werden müssen.
Daniel Pryden
4

Wenn Sie ein Programm in einer Programmiersprache schreiben, konvertiert ein anderes Programm die Symbole in Ihrem Programm in Symbole, die der Computer versteht. Manchmal dauert dies mehrere Schritte. Zum Beispiel in C:

  1. Der Benutzer schreibt das Programm in der Hochsprache (C), die von der CPU nicht verstanden wird, aber vom Programmierer direkt verstanden wird (wir hoffen!).

  2. Der Compiler konvertiert C in die Assmebly-Sprache, die von der CPU nicht direkt verstanden wird, aber einfach in etwas anderes konvertiert werden kann.

  3. Assempler wandelt Assembly in eine Folge von Binärcodes um, die von der CPU direkt verstanden werden. Einige Compiler überspringen den obigen Schritt (Schritt 2) und erzeugen die kompilierte Binärdatei direkt aus dem Quellcode.

Um sicherzustellen, dass der Computer Ihr Programm versteht, gibt der Compiler oder Interpreter einen Fehler aus und stoppt normalerweise, wenn er auf etwas stößt, das nicht kompilierbar ist, z. B. auf einen Syntaxfehler. Wenn Ihr Programm nicht kompiliert werden kann, kann es niemals das Stadium erreichen, in dem Ihr Programm versucht, es auszuführen, und scheitert, weil es es nicht "verstanden" hat.

Um eine neue Sprache zu erstellen, müssen Sie zuerst Ihre übergeordnete Sprache entwerfen und dann eine Möglichkeit finden, die Symbole Ihrer neuen Sprache den Assemblersprachenbefehlen zuzuordnen, die Ihre CPU versteht.

FrustratedWithFormsDesigner
quelle
2
Nicht wirklich; Moderne Compiler machen nicht Schritt 2 und erzeugen nur direkt Binärcode. Assembly und Binärcode sind jedoch sowieso fast gleichwertig. Sie können Binärcode mit sehr hoher Wiedergabetreue zerlegen (zurück in Assembly konvertieren).
MSalters