Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

11

06.06.2014, 13:16

Durch den Unterstrich erkenne ich dass es sich um eine Variable handelt, dann kann man theoretisch auch Schlüsselwörter nutzen:

Quellcode

1
_if = 123


Der Name selbst wird dann in einen Index umgewandelt und auf einen virtuellen Arbeitsspeicher angewandt.
Hätte ich natürlich anders lösen können, aber das erscheint mir mit am einfachsten. Und für die Sprache selbst ist es auch ganz nett, finde ich zumindest.

Zitat von »Architekt«

Nein, ich meinte, dass du Terme vor dem Parsen in die Polnische Notation überführst.

Hmm, also wenn du dir den Code ansiehst, z.B. für die Zuweisung:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
Expression* Variable::accept(const std::string &token){
    if(name.empty()){
        name = token;
    }
    else if(token == "="){
        parent = new Assign();
        parent->left = this;

        return parent;
    }

    return this;
}


Siehst du das es "Rückwärts" funktioniert. Also ich sehe erst, aha eine Variable und dann das =.
Da der compiler immer accept für den aktuellen Knoten aufruft kann ich so einen Schritt zurück gehen.

Also ist das denke ich mal nicht nötig, oder übersehe ich einen wichtigen Vorteil der mir sonst später das Genick bricht?

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

12

07.06.2014, 14:51

So inzwischen geht auch das (wuhhh :P):

Quellcode

1
2
3
4
5
6
_a = 1
print _a
_b = 2
_a = 4
print _b
print _a


Byte code:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
setID  0
val    1
push  
assign
setID  0
print  1    # tatsächliche Ausgabe
setID  1
val    2
push  
assign
setID  0
val    4
push  
assign
setID  1
print  2
setID  0
print  4


Ich finds nur noch nicht so dolle dass print eine Variable im Speicher ausgibt.
Besser wäre evt. top() vom Stack oder?

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »DeKugelschieber« (07.06.2014, 14:56)


Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

13

07.06.2014, 15:41

Was hilft wie ich finde ist wenn du erst mal mit einem Parsergenerator anfängst. Dann bekommst du ein wenig Gefühl für Grammatiken und kannst dich erst mal ein wenig ausleben. Danach kannst du dich dann vielleicht etwas einfacher an einen eigenen Parser machen. Ein Stichwort ist Top Down Parsing. Das dem Prinzip gehen die meisten von Hand geschriebenen Parser vor. Einfach weil es etwas einfacher ist.
Was deine Frage zu Print angeht, so musst du überlegen was Print machen soll. Soll Print immer den Wert oben auf dem Stack zurück geben, oder doch eigentlich eine Variable aus dem Speicher ausgeben? Klar gibt es den Sonderfall bei welchem die gewünschte Variable direkt oben liegt, aber das wäre dann möglicherweise eher was für Optimierung und ich denke da wird sich einiges finden wo es mehr Sinn macht zu optimieren.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

14

07.06.2014, 15:55

Was meinst du mit Parsergenerator?

Hmm das print lasse ich erst mal so. Im moment habe speichere ich mit setID den Index der Variable zwischen, der dann von print ausgegeben wird.
Ganz einfach eigentlich.

[Edit]

Nee moment, macht gar keinen Sinn mit print aus den Variablen auszugeben. Was wenn ich sowas habe:

Quellcode

1
print 5


Soll ja auch gehen.
Also dann doch lieber in sowas umwandeln:

Quellcode

1
2
3
val 5
push
print

LukasBanana

Alter Hase

Beiträge: 1 097

Beruf: Shader Tools Programmer

  • Private Nachricht senden

15

07.06.2014, 16:29

Was meinst du mit Parsergenerator?

An der TU Darmstadt haben wir in der "Compiler 1" Vorlesung erstmal gelernt, wie man einen Compiler von Grund auf selbst entwicklt,
also Lexer, Parse, Context-Analyzer und Code-Generator.

Am Ende wurde uns noch der Parser-Generator ANTLRv4 vorgestellt.
Mit dem beschäftige ich mich auch gerade etwas, aber ich habe den Eindruck, dass die aktuelle Version noch nicht vollständig C++ Parser generieren kann.

Mit so einem Parser Generator kannst du die Grammatik deiner gewünschten Sprache definieren
und ANTLR generiert dir dann entweder Java, C# oder C++ Code, der den jeweiligen Lexer und Parser implementiert.
D.h. du kannst dir die ganze Arbeit mit dem Entwickeln eines Lexers und Parsers ersparen ;-)

Speziell ANTLR bietet dir sogar die Möglichkeit einen Visitor und/oder Listener für deinen AST (Abstract-Syntax-Tree) zu generieren,
mit dem du dann deinen Syntax Baum komfortabel durchlaufen kannst.

Gruß,
Lukas

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »LukasBanana« (12.07.2014, 15:34)


DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

16

07.06.2014, 16:50

Ah achso, nee das nimmt ja den ganzen Spaß aus der Sache :D

Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

17

07.06.2014, 17:46

Erstmal. Aber vor allem ist es eine solide Übung. Ich habe für meine Skriptsprache auch erstmal eine Grammatik im Gold Parsing System realisiert. Das hilft enorm dabei, schnell Fehler zu finden, und schärft das Gespür dafür, wie der selbstgebaute Parser/Lexer aussehen muss. Im Übrigen baust du momentan einen Interpreter, keine VM. Und dein "Bytecode" ist eine Assembly. So viel zur Taxonomie.

Weiterhin kannst du den Unterstrich wie in Ruby umsetzen. Dort sind die @@, @, und $ Bestandteil des Variablen-Namens. Das sind dann weniger Token zu verarbeiten, und vor allem deutlich weniger obsolete Token. Du könntest nämlich einfach ein Unterstrich-Bit im ID-Token setzen. Vor allem ersparste dir so reichlich Sonderfälle. Wenn _ ein eigenes Token ist, so wird eine Variable _if vom Lexer als z.B. TK_VAR, KW_IF verarbeitet. Der Syntax-Baum und deine Variablen-Tabellen müssen nun den Sonderfall beachten, dass beim Unterstrich auch Keywords gültige Variablennamen sind. Ziemlich unpraktisch.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Evrey« (07.06.2014, 17:54) aus folgendem Grund: Mir fiel noch mehr Zeugs ein.


DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

18

07.06.2014, 17:51

Gut ich kanns mir ja mal ansehen.

Aber ob der Unterstrich jetzt ein Token ist oder nicht, kann mir relativ egal sein, da das ganze kein Interpreter ist.
Zumindest nicht streng genommen, ich finde die Definition da etwas schwammig. Der Code wird ja nicht analysiert, er wird einfach binär umgewandelt und dann durch eine switch gejagt. Die compilier Zeit ist daher relativ egal.

[Edit]

Achso mir ist gerade aufgefallen dass meine Posts verwirren könnten, den "Assembler" Code gibt es so natürlich nicht, nur binär.
Ich lasse mir den momentan nur so ausgeben damit ich sehe was passiert.

Das Programm von oben sieht so aus:


(Link)



Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »DeKugelschieber« (07.06.2014, 17:59)


Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

19

07.06.2014, 17:56

Ach sou. Ich hatte das zwischendurch so gelesen, dass du wohl den Syntaxbaum abfrühstückst. Stupid me.

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Beiträge: 1 223

Wohnort: Deutschland Bayern

Beruf: Schüler

  • Private Nachricht senden

20

07.06.2014, 17:58

Wenn du JIT kompilieren willst oder binär Code kompilieren willst, solltest du mal einen Blick auf den LLVM werfen.
Der kann dir all dir die Aufgabe abnehmen, Maschinencode für alle möglichen Platformen zu generieren mit allen drum und dran. Er bietet auch viele fertige Optimierungsmethoden die du sehr einfach darauf anwenden kannst. http://llvm.org/docs/tutorial/

Werbeanzeige