Ergebnis 1 bis 20 von 25

Thema: Wie entsteht eine neue Programmiersprache?

Hybrid-Darstellung

Vorheriger Beitrag Vorheriger Beitrag   Nächster Beitrag Nächster Beitrag
  1. #1

    Wie entsteht eine neue Programmiersprache?

    Schönen guten Abend.

    Eine kleine Frage aus reiner Neugierde.
    Wie entwickelt man eine neue Programmiersprache? Mich interessiert es einfach wie soetwas entsteht und was dafür nötig ist? Erfahrung und Kenntnis ist natürlich selbstverständlich.

    Vielen Dank für alle Antworten.
    Cornix.

  2. #2
    Außer Zeit und Programmiererfahrung nicht viel. Einen Compiler für einen Programmiersprache mit sehr eingeschränkter Funktionalität kann man schnell schreiben. Du solltest dir aber überlegen für welchen Prozessor du den Compiler schreibst, alternativ kannst du natürlich auch z.B. JAVA-Bytecode produzieren.
    Das Buch von Wirth ist dazu übrigens ganz gut: http://www-old.oberon.ethz.ch/WirthPubl/CBEAll.pdf

  3. #3
    Idealerweise entwickelt man eine Programmiersprache erstmal seeehr lange auf dem "Papier" und nicht am Computer (in der IDE). Planung ist der erste Schritt: Was wird das für eine Sprache (prozedural, OOP, funktional,...), was für Features soll die Sprache haben, Syntax und Semantik (wenn einem langweilig ist auch formell) definieren - das ist die halbe Miete. Die Umsetzung ist dann, ich will nicht sagen einfach, aber der leichtere Teil. Eine Programmiersprache, die einfach "gemacht" wurde, ohne eingehende Planung, und sich dann entwickelt hat, haben wir schon - nennt sich C. Unintuitive Syntax für später hinzugefügte Features sind die Folge.

  4. #4
    Vielen Dank für die Antworten.
    Allerdings geht es mir hier nicht darum eine eigene Sprache zu entwickeln. Meine Fähigkeiten in diesem Bereich sind, wenn ich nicht damit schon übertreibe, mehr als dürftig.
    Es interessiert mich lediglich die technische Komponente dahinter. Ich kann mir nur schwer vorstellen wie sich so eine Programmiersprache entwickelt.

    So weit ich es verstanden habe arbeitet ein Computer lediglich mit Einsen und Nullen. Die Programmiersprache mit der wir schreiben muss daher wohl für den Computer leserlich gemacht werden indem sie in dieses Binärformat übertragen wird. Doch wie dies gemacht wird kann ich mir garnicht vorstellen.
    Und wenn solch ein Compiler in einer anderen Programmiersprache geschrieben werden kann, wie wurde dann der erste Compiler geschrieben? Es muss ja schier unmöglichsein für einen Menschen direkt mit Einsen und Nullen ein Programm zu schreiben. Das ist für mich einfach undenkbar.

  5. #5
    Zitat Zitat von Cornix Beitrag anzeigen
    [...] Und wenn solch ein Compiler in einer anderen Programmiersprache geschrieben werden kann, wie wurde dann der erste Compiler geschrieben? Es muss ja schier unmöglichsein für einen Menschen direkt mit Einsen und Nullen ein Programm zu schreiben. Das ist für mich einfach undenkbar.
    Und doch ist genau so gegangen: zuerst schreibt irgendein Nerd mit zu viel Zeit und Hintergrundwissen eine sehr einfache, grundlegende Programmiersprache mit Einsen und Nullen (beispielsweise Assembler), die nicht viel mehr kann, als für Menschen halbwegs verständliche Begriffe in Einsen und Nullen zu übersetzen. Dann kommt irgendwer anders daher, und nutzt diese Programmiersprache, um eine höhere zu programmieren, die stärker abstrahiert ist, und dementsprechend für Menschen einfacher zu verstehen. Man wiederhole diesen Vorgang ein paar mal, und schon hat man die Möglichkeit, mit einer einzigen Zeile Quelltext in einer Hochsprache eine wahre Kaskade von Einsen und Nullen in seinem Rechner heraufzubeschwören. =)

  6. #6
    Geht es darum wie ein Computer aus Einsen und Nullen komplexere Befehle ausführt, oder um den Aufbau eines Compilers?
    Das wird glücklicherweise mittlerweile gut getrennt.
    Der CPU-Hersteller gibt einen bestimmten Befehlssatz vor, oft mit nur rudimentären Operationen (Speicherzugriffe, Einfache Rechenoperationen, Shiften und ein paar Sprunganweisungen).
    Für einen Compiler ist das natürlich das begehrte Zielformat.

    Der Schritt vom Quellcode zum fertigen Programm ist relativ komplex und wird daher in verschiedene Schritte unterteilt.
    1. Lexing
    Der Quellcode wird mithilfe von regulären Ausdrücken in eine Reihe von synaktischen Tokens zerlegt. Statt eines Zeichenstroms kriegt der nachfolgende Parser die einzelnen syntaktischen Bausteine Stück für Stück. (Statt, "if x > 1 then y" liest der parser: if-token, identifier, greater, constant, then, identifier)
    In dem Schritt werden auch Whitespaces und Kommentare "überlesen".
    2. Parser
    Der Parser arbeitet auf diesem Tokenstream des Lexers und muss entscheiden ob der Input ein syntaktisch korrektes Programm darstellt oder nicht. Hier wird mit kontextfreien Grammatiken (bzw. mit etwas eingeschränkten kfG) gearbeitet. Der Parser baut als Output einen Syntax-Baum auf (genau genommen einen sogenanntne "Abstrakten Syntax Baum" AST, da der konkret zum Parsen verwendete Syntaxbaum für die spätere Arbeit etwas unhandlich ist)

    In der Regel spricht man direkt den Parser an, der dann jeweils den Lexer aufruft um das nächste Token zu kriegen. Da Parsing ein relativ komplexes Problem ist baut man mittlerweile eher selten Parser von Hand. Stattdessen gibt es Programme, die für eine gegebene Grammatik einen entsprechenden Parser oder Lexer (oder beides) generieren.

    Ich benutz jetzt mal eine einfache Anweisung wie "y = x + 2" als Beispiel.
    Im AST hätten wir also hier einen Zuweisungsknoten, dessen linkes Kind ein Identifier Knoten wäre (die variable y), der rechte Knoten wäre ein Add-Knoten, mit den jeweiligen Kindern: Identifier-Knoten (variable x) und Konstante-Knoten (die 2).
    Auf diesem AST kann man nun Typchecking (stimmen die typen der kinder des add-knoten für eine addition, ist der ergebene typ des Add-Knotens kompatibel mit dem typ von y) und diverse Optimierungen vornehmen.

    Am Ende ist es relativ "simpel" für einen bestimmten Knoten im AST den entsprechenden Assemblercode zu generieren. Für obiges Beispiel wäre das zB:
    1. Lade die Adresse des linken Kinderknoten (des Ziels) in ein Register A
    2. Führe den Code zur Berechnung des Rechten Kinderknotens aus (Ergebnis steht in einem fest definierten Register B)
    3. Kopiere den Wert aus B an die Adresse, die in A steht

    Ich hab auf der Platte noch den Compiler (Source Code) für eine einfache Programmiersprache liegen, den wir letztes Semester als Projekt schreiben mussten. Der Compiler ist in Java geschrieben und erzeugt als Output Code für einen Assembler, der das ganze in Java-bytecode übersetzt. Falls du Interesse hast, kannst du ihn gerne haben =).

  7. #7
    Sehr detailierte Antworten, vielen Dank.
    Ich denke ich konnte meinen Kopf damit ein klein wenig erleichtern.

    Und vielen Dank für das Angebot, allerdings denke ich ist dies nicht notwendig MagicMagor.

  8. #8
    Ich wäre allerdings durchaus interessiert

  9. #9
    Da wirft sich mir allerdings doch noch eine Frage auf.
    Läuft ein Interpreter mit dem selben Prinzip? Es müsste doch noch ein klein wenig anders sein schätze ich.
    Wird eine Zeile nach der anderen Compiliert? Oder der Befehl welcher gerade ausgeführt wird? Oder die ganze Methode die gerade ausgeführt wird?
    Denn falls es nur Zeilen / Funktionsweise compiliert werden würde dann müssten if/then/else Strukturen etwas komplizierter ausfallen oder täusche ich mich?

  10. #10
    Die Compiler arbeiten quasi immer nach dem selben Schema.
    If-then-else sind nichts weiteres als sog. Sprungbefehle. Sie springen von einem Speicherbereich zu nächsten.
    Du kennst vielleicht den goto-Befehl aus einigen Sprachen. If-then-else ist im Grunde nichts anderes, als ein goto-Befehl.

    Ein Compiler ist ja kein Zeileninterpreter, wie bei Scriptsprachen, sondern er übersetzt das komplette Programm. Wenn das Programm gestartet wird, wird das Programm in den Arbeitsspeicher geladen und dort ausgeführt. Somit gibt es für den Prozessor auch zwischen Variable und Funktion keinen Unterschied. Beides sind nur Bereiche im Speicher, die der Prozessor abarbeitet. Vielleicht hast du auch schonmal was von (Funktions-)Zeigern gehört. Also ein Zeiger, der auf eine Variable oder Funktion zeigen.

    Zeileninterpreter interpretieren nur die Zeile, in der sich sich grad befinden. Bei If-Then-Else würde er dann mehrere Zeilen, zum Else-teil überspringen, wenn die Bedingung nicht erfüllt ist.

  11. #11
    Vielen Dank, ganz genau scheine ich das aber noch nicht verstanden zu haben glaube ich.
    Was bedeutet dies konkret bei einer Programmiersprache wie beispielsweise Ruby (welche in dem RPG-Maker XP verwendet wird), der Code wird nunmal nicht compiliert wie in beispielsweise C++, wenn man das Programm startet was passiert dann damit? Wird der Code Zeile für Zeile eingelesen?

Berechtigungen

  • Neue Themen erstellen: Nein
  • Themen beantworten: Nein
  • Anhänge hochladen: Nein
  • Beiträge bearbeiten: Nein
  •