Benutzer:Humbug/Programmiersprache
Eine Programmiersprache ist eine Notation für Computerprogramme; sie dient sowohl dazu, diese während und nach ihrer Entwicklung (Programmierung) darzustellen als auch dazu, die daraus resultierenden Programme zur Ausführung an Rechensysteme zu übermitteln. Da nur die Maschinensprache vom Rechner unmittelbar ausführbar ist, bedürfen Programme in jeder anderen Programmiersprache einer maschinellen Weiterverarbeitung durch Übersetzung oder Interpretation; eine Programmiersprache muss also für eine maschinelle Analyse geeignet sein, was zahlreiche Einschränkungen zur Folge hat.
Programmiersprachen sollen die Programmierung nicht nur ermöglichen, sondern so gut wie möglich erleichtern. So hat sich die Entwicklung der Programmiersprachen seit den 1940er Jahren im Spannungsfeld von Übersetzbarkeit und Bequemlichkeit (Lesbarkeit, Knappheit, Sicherheit etc.) vollzogen. Dieses Spannungsfeld besteht auch zwischen Universalität und Anwendungsorientierung, denn je näher eine Programmiersprache an der Anwendung ist, desto bequemer und leichter ist sie anzuwenden, desto enger ist aber auch in der Regel ihr Anwendungsgebiet und desto unwirtschaftlicher ist sie.
Übersicht
In der Regel hat jeder Rechnertyp seine eigene Maschinensprache, die man aber nur selten direkt benutzt; meist setzt man Hilfsprogramme ein (summarisch Assembler genannt), um in einer lesbareren (Assembler-) Sprache zu programmieren. Diese Programme erledigen unter anderem:
- Das Abzählen von Speicherzellen und Führen der Speicherbelegungstabelle,
- die Umwandlung von Zahlen, Texten, Maschinenbefehlen und (symbolischen) Adressen in die (binäre) Interndarstellung,
- das textliche Einfügen vorformulierter und parametrisierbarer Programmabschnitte (Makros)
Ein anderes Hilfsprogramm, der Binder, erlaubt es zudem, ein Programm um bereits übersetzte Unterprogramme zu ergänzen. Beide Möglichkeiten zusammengenommen erlauben eine Programmierung unter weitgehender Befreiung von repetitiven Details, wenn die entsprechenden Unterprogramme und Makros erst einmal geschrieben sind.
Fast immer ist es die Ein-/Ausgabe-Programmierung, die so als erstes dem gewöhnlichen Programmierer abgenommen wird, weil sie als besonders schwierig gilt, danach kommen mathematische Unterprogramme, deren Definition einigermaßen klar ist. Auf andere Gebiete ließen sich diese Methoden hingegen nicht mit vergleichbarem Erfolg übertragen.
Von dort zu einer einfachen höheren oder problemorientierten Sprache zu gelangen, ist kein großer Schritt mehr; Fortran beispielsweise bietet zunächst zusätzlich nur die Möglichkeit, einfach geformte arithmetische Ausdrücke in Folgen von Maschinenbefehlen zu übersetzen. So gab es schon bald eine große Zahl an Spezialsprachen für die verschiedensten Anwendungsgebiete, auch die Klassiker, Fortran, Lisp und Cobol, fingen so an. Damit steigt die Effizienz der Programmierer und die Portabilität der Programme; dafür nimmt man eine anfänglich erheblich geringere Leistungsfähigkeit der erzeugten Programme in Kauf. (Inzwischen hat man hier große Fortschritte gemacht.) Manche Sprachen sind so erfolgreich, dass sie wachsen und breitere Anwendung finden; immer wieder sind auch Sprachen mit dem Anspruch entworfen worden, Mehrzweck- und Breitbandsprachen zu sein, oft mit bescheidenem Erfolg (PL/1, Ada, Algol 68).
Nach der Entwicklung von Algol 60, nur einige Jahre nach Fortran, begann man zu fragen, welches Berechnungsmodell zweckmäßig zugrundezulegen und wie es systematisch auszugestalten sei (vgl. ISWIM).
- In der Rückschau kann man die bisherige Entwicklung der Programmiersprachen in Generationen einteilen, siehe das eigene Unterkapitel weiter unten.
Anforderungen
- Einfachheit, Regelmäßigkeit, Orthogonalität
- Abstraktionsöglichkeiten, Klarheit, Sicherheit
- Modularität, Kapselung, Ausdruckfähigkeit
- Effizienz
Panorama
Die überragende Bedeutung von Programmiersprachen für die Informatik drückt sich auch in der Vielfalt der Ausprägungen und der Breite der Anwendungen von Programmiersprachen aus.
- Systemprogrammiersprachen, Assemblersprachen, C, Bliss usw. erlauben eine hardwarenahe Programmierung.
- Datenbanksprachen sind für den Einsatz in und die Abfrage von Datenbanken gedacht.
- Skriptsprachen dienen zur einfachen Steuerung von Rechnern, wie bei der Stapelverarbeitung.
- Spezifikationssprachen dienen der Beschreibung von (u.a.) Programmen mit mathematischen Mitteln, Breitbandsprachen integrieren die Spezifikation und Implementation in einer Sprache.
Elemente einer Programmiersprache
- Hauptartikel: Programmiersprachelemente
Rechenvorschriften
Die Bedeutung der Rechenvorschrift wurde Anfang der 60er Jahre vor allem durch die Arbeiten von Landin erkannt. Die RV leistet dreierlei:
- die Operationen angeben (Term, Rechenbaum)
- die Wertebelegung angeben (Umgebung und Parametrisierung)
- das Ganze benennen (aber auch anonyme Fkt.)
„Funktionsabstraktion“ danach entsprechend Datenabstraktion, Modulabstraktion etc. (Abstraktionsprinzip, Tennent)
Let u Where Klauseln
Moduln
Typsystem
- Hauptartikel: Typsystem
Daten, Datentypen und Typisierung
Fast alle Programmiersprachen kennen Typangaben im Programmtext (die sich auf Programmteile oder auch Werte beziehen können). Damit werden vor allem zwei Zwecke verfolgt:
- Deskriptiv dienen Typangaben der Erleichterung der Programmierung und Entlastung der Notation; es braucht so nicht jede Operation vollständig spezifiziert zu werden.
- Präskriptiv dienen sie dazu, bestimmte Operationen auszuschließen, durchaus nicht nur zur Fehlervermeidung, sondern (vor allem im Zusammenhang mit Abstrakten Datentypen) auch zur Zusicherung von Sicherheitseigenschaften.
So erspart eine Feldvereinbarung nicht nur (deskriptiv) die Angabe der Speicherabbildungsfunktion bei jedem Zugriff, sondern erlaubt (präskriptiv) auch die automatische Prüfung auf Einhaltung der Feldgrenzen. Das sichere Typsystem der Programmiersprache ML bildet die Grundlage für die Korrektheit der in ihr programmierten Beweissysteme (LCF, HOL, Isabelle); in ähnlicher Weise versucht man jetzt auch die Sicherheit von Betriebssystemen zu gewährleisten.[1] Schließlich ermöglichen erst Typangaben das populäre Überladen von Bezeichnern.
Grundtypen
Pragmatik
Implementation von Programmiersprachen
- Hauptartikel: Übersetzerbau
Die Tätigkeit eines Übersetzers kann als eine Folge von Transformationen („Phasen“) veranschaulicht werden, die ein „Quellprogramm“ stufenweise in ein „Objektprogramm“ umwandeln, auch wenn diese in der Praxis ineinander verschachtelt ablaufen. Das Quellprogramm ist dabei eine Zeichenfolge, die einer (kontextfreien) Grammatik genügt (der „Syntax“ einer Programmiersprache); die Größe einer solchen „Übersetzungseinheit“ liegt zwischen einer einzelnen Anweisung für interaktive Sprachen und einem ganzen Programm, ist meist aber ein Unterprogramm oder Modul („modulare Programmierung“).
Frontend
Als Frontend bezeichnet man die analytischen Phasen, die hauptsächlich von der Programmiersprache bestimmt sind.
- Die Symbolerkennung liest die Eingabe von links nach rechts (daher Scanner) und erkennt dabei einfache, durch reguläre Ausdrücke beschriebene Zeichenfolgen, z. B. „3.1415“ (Zahl), „if“ (Wortsymbol), „MainWindow“ (Bezeichner), „+“ (Operator), „{“ (Sonderzeichen). Die Bezeichner und Literale werden dabei in einer Symboltabelle abgelegt bzw. nachgesehen.
- Die Phrasenerkennung (Parser) erhält eine Folge von (Terminal-) Symbolen (tokens) als Eingabe und rekonstruiert daraus den dazugehörigen Ableitungsbaum. Insgesamt ist dies wesentlich schwieriger als die Symbolerkennung; dazu trägt einerseits die rekursive Struktur der Grammatik bei. Andererseits treten an die Stelle von Eingabezeichen Terminal- und Nichtterminalsymbole. Letztere bilden die Knoten des Ableitungsbaumes und stellen jeweils einen erkannten („reduzierten“) Ableitungsschritt dar.
- Die Symboltabelle enthält die in einer Übesetzungseinheit gebrauchten sowie die aus einem Bibliotheksmodul importierten Bezeichner. Der Zugriff erfolgt dabei unter Berücksichtigung der Sichtbarkeitsregeln der jeweiligen Sprache.
- Der Ableitungsbaum enthält viele überflüssige Details, etwa zur Implementation von Vorrangregeln („Punkt vor Strich“) oder zur u. U. variantenreichen Strukturierung des Programms („syntactic sugar“). Darum bereinigt man ihn zum „Abstract Syntax Tree“ (AST). Dieser stellt aber zunächst nur ein äußeres Gerüst des Programmes dar.
- Die semantische Analyse ergänzt („dekoriert“) darum den AST durch Informationen aus der Symboltabelle und aus dem Kontext (den übergeordneten Knoten). Zugleich werden die zahlreichen „Kontextbedingungen“ überprüft, die ein korrektes Programm erfüllen muss. So ergänzt wird der AST zur Interndarstellung des Programmes („Programmbaum“).
Backend
Im weiteren Sinne ist das Backend der synthetische Teil des Übersetzers, der aber nur zu einem Teil vom Zielsystem abhängt. Daher liegt es nahe,
Geschichte
- Hauptartikel: Geschichte der Programmiersprachen
Zur Vorgeschichte der Programmiersprachen kann man von praktischer Seite die zahlreichen Notationen zählen, die sowohl in der Fernmeldetechnik (Morsekode) als auch zur Steuerung von Maschinen (Jacquard-Webstuhl) entwickelt worden waren; dann die Assemblersprachen der ersten Rechner, die doch nur deren Weiterententwicklung waren. Von theoretischer Seite zählen dazu die vielen Präzisierungen des Algorithmusbegriffs, von denen der λ-Kalkül die bei weitem bedeutendste ist. Auch Zuses Plankalkül gehört hierhin, denn er ist dem minimalistischen Ansatz der Theoretiker verpflichtet (Bit als Grundbaustein).
In einer ersten Phase wurden ab Mitte der 1950er Jahre unzählige Sprachen[2] entwickelt, die praktisch an gegebenen Aufgaben und Mitteln orientiert waren. Seit der Entwicklung von Algol 60 (1958-1963) ist die Aufgabe des Übersetzerbaus in der praktischen Informatik etabliert und wird zunächst mit Schwerpunkt Syntax (-erkennung, Parser) intensiv bearbeitet. Auf der praktischen Seite wurden erweiterte Datentypen wie Verbunde, Zeichenketten und „Zeiger“ eingeführt (konsequent z. B. in Algol 68).
Ergänzungen
Sprachgenerationen
Man hat die Maschinen-, Assembler- und höheren Programmiersprachen auch als Sprachen der ersten bis dritten Generation bezeichnet; auch in Analogie zu den gleichzeitigen Hardwaregenerationen. Als vierte Generation wurden verschiedenste Systeme beworben, die mit Programmgeneratoren und Hilfsprogrammen z.B. zur Gestaltung von Bildschirmmasken (screen painter) ausgestattet waren. Die Sprache der fünften Generation schließlich sollte in den 1980er Jahren im Sinne des Fifth Generation Computing Concurrent-Prolog sein.
Hello World
Ein beliebter Einstieg in eine noch unbekannte Programmiersprache ist es, mit ihr die sehr einfache Aufgabe zu lösen, den Text Hello World (oder deutsch „Hallo Welt“) auf den Bildschirm oder ein anderes Ausgabegerät auszugeben, siehe bei Hallo-Welt-Programm. Entsprechend gibt es Listen und eigene Webseiten[3], die Lösungen in allen möglichen Programmiersprachen gegenüberstellen.
Siehe auch
Literatur
- Friedrich Ludwig Bauer, Hans Wössner: Algorithmische Sprache und Programmentwicklung. Springer, Berlin 11981, 21984, ISBN 3-540-12962-6.
- Robert Harper: Practical Foundations for Programming Languages. – Manuscript 2009-04-16
- Benjamin C. Pierce: Types and programming languages. MIT Press, Cambridge, Mass. 2002, ISBN 0-262-(?!).
- Benjamin C. Pierce [Hg.]: Advanced topics in types and programming languages. MIT Press, Cambridge, Mass. 2005, ISBN 0-262-16228-8.
- John C. Reynolds: Theories of programming languages. Cambridge Univ. Press, Cambridge 1998, ISBN 0-521-59414-6.
- Peter van Roy, Seif Haridi: Concepts, techniques, and models of computer programming. MIT Press, Cambridge, Mass. 2004, ISBN 0-262-22069-5.
- Michael L. Scott: Programming language pragmatics. 2. Auflage. Elsevier, Morgan Kaufmann, Amsterdam 2006, ISBN 0-12-633951-1.
Weblinks
- Literate Programs Ein Wiki mit Beispielen in vielen Sprachen (englisch)
Einzelnachweise
- ↑ Vgl. Sprachbasiertes System – z. B. Singularity basierend auf der Programmiersprache Sing#, JX für Java, Coyotos mit der Sprache BitC.
- ↑ Um 1965 zählte man 1700, vgl. ISWIM.
- ↑ Auflistung von Hello-World-Programmen nach Programmiersprachen