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

buggypixels

Treue Seele

Beiträge: 125

Wohnort: Meerbusch

Beruf: Programmierer

  • Private Nachricht senden

41

16.06.2014, 13:37

Vielleicht hilft Dir das weiter:

http://bitsquid.blogspot.de/2011/03/putt…rogramming.html

Der Code ist vor allem auch wirklich sehr gut.

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

42

17.06.2014, 10:00

Hmm danke, ist mir aber bekannt, bis auf das mit den Tokens in die andere Notation zu überführen.

Ich habs jetzt erstmal rekursiv hinbekommen Variablen zu setzen und mit "print" auszugeben.
Das einzige was ich noch etwas merkwürdig finde sind Werte. Die erkennt man ja nicht einfach so, oder ich müsste es prüfen. Dafür habe ich zwar eine Funktion, aber wenns ginge würde ichs gerne vermeiden.

Ein assign sieht dann so aus:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// _x = 1

Expression* Snippet::parseAssign(){
    auto assign = new Assign();

    assign->left = parseExpr(); // links wird der Teil hinter dem = angehangen (parseExpr findet aktuell Variablen und Werte)

    auto current = currentToken; // aktuellen Index speichern
    currentToken = leftToken; // auf Anfang des linken Bereichs springen

    assign->right = parseVariable(); // Variablennamen rechts anhängen

    currentToken = current; // wieder zurück...
    leftToken = current;

    assign->left->parent = assign; // Tree vervollständigen
    assign->right->parent = assign;

    return assign; // assign zurückgeben
}

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

43

17.06.2014, 11:01

Mit linkem Parameter meinte ich als Beispiel:
2+3 (linker Parameter wäre 2)
Einfach umstellen darfst du solche Terme nicht. Stell dir vor du müsstest alles Klammern.
2 + ( 3 - 4 ) ist etwas anderes als ( 2 + 3 ) - 4.
Bindet ein Operator stärker links so würdest du bei 2 + 3 - 4 den linken Term erhalten. Bindet er stärker rechts so erhältstdu den rechten Term. Versuch vielleicht nicht direkt das ganze als Code umzusetzen sondern bastel dir vorher eine BNF zu deiner Grammatik. Ich finde es so zumindest einfacher. Und dabei am besten nicht zu viel auf einmal machen. Lieber eine Sache machen und gucken ob alles korrekt ist und dann nach und nach erweitern als anders herum.
„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

44

19.06.2014, 16:55

Ja okay, ich glaube ich verstehs langsam.
Nur wie ich den AST so erstelle, dass die Reihenfolge bei zum Beispiel den aritmetischen Operatoren auch stimmt, verstehe ich noch nicht so ganz.

Beispiel:

Quellcode

1
_x = 1-2+3

Sollte natürlich 2 ergeben. Ergibt es aber nicht, da bei der Syntax:

Quellcode

1
2
3
4
5
A -> B
B -> C=D
C -> _                  Variable
D -> E+E | E-E | E
E -> int                Wert

Folgender Syntax Baum entsteht:

[Edit] Woops erst vermalt, sollte jetzt passen:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
           A
           |
           B
           |
           =
          / \
         D   C
        /
       E-E
        |
        -
       / \
      E   E
     /     \
    1      E+E
            |
            +
           / \
          E   E
         /     \
        2       3

Und dieser bottom-up geparsed wird. Also in Pseudo-Assembler wieder (Ausgabe meiner VM):

Quellcode

1
2
3
4
5
6
7
8
9
val     1
val     2
val     3
add     =        5
sub     =        -4
use     0                                   # 0 = _x
assign
----------------
-4                      # Wert in _x

Hmm. Das add und sub müsste getauscht werden, oder der Baum allgemein direkt anders erstellt werden.

Damit ihr nicht immer auf GitHub rumdümpeln müsst, hier mal der Parser Code.
Ich habe extra Kommentare eingefügt damit man nachvollziehen kann was ich mir dabei gedacht habe.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
const std::string& Snippet::get() // aktuelle Token als String
bool Snippet::next() // weiter, wenn Ende der Token erreicht -> false
bool Snippet::accept(const std::string &token) // gibt true wenn der aktuelle Token übereinstimmt
bool Snippet::expect(const std::string &token) // wie accept(), wirft allerdings Fehler und erhöht Token-Zähler

Expression* Snippet::parseExpr(){
    auto result = parseAssign(); // aktuell können wir nur das

    return result;
}

Expression* Snippet::parseValue(){ // liest Wert in Value Knoten
    auto value = new Value();
    value->value = get();
    next();

    return value;
}

Expression* Snippet::parseVariable(){ // liest Namen in Variable Knoten
    expect("_");

    auto variable = new Variable();
    variable->name = get();
    next();

    return variable;
}

Expression* Snippet::parseAssign(){
    auto result = new Assign();

    result->right = parseVariable(); // die Variable wird rechts angehangen, damit sie direkt vor dem assign gebunden wird
    expect("=");
    result->left = parseArith(); // links alles andere (aktuell nur ein aritmetischer Ausdruck)

    return result;
}

Expression* Snippet::parseArith(){ // akzeptiert + oder -, gibt ansonsten einen Wert zurück (Value Knoten)
    Expression* result = 0;

    auto current = currentToken;
    next(); // wir müssen "in die Zukunft schauen"

    if(accept("+")){
        currentToken = current; // und später wieder zurück...

        result = new Add();

        result->left = parseValue(); // links muss ein Wert stehen
        expect("+"); // dann muss der Operator kommen (den wir vorher akzeptiert haben)
        result->right = parseArith(); // und jetzt kann ein anderer Ausdruck kommen...
    }
    else if(accept("-")){
        currentToken = current;

        result = new Sub();

        result->left = parseValue();
        expect("-");
        result->right = parseArith();
    }
    else{
        currentToken = current;

        result = parseValue(); // ich gehe noch davon aus dass es sich somit um einen Wert handeln muss
    }

    return result;
}

Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von »DeKugelschieber« (19.06.2014, 17:07)


drakon

Supermoderator

Beiträge: 6 513

Wohnort: Schweiz

Beruf: Entrepreneur

  • Private Nachricht senden

45

19.06.2014, 18:12

In dem Fall könntest du auch ein "-" einfach als "+" interpretieren und das Minus zur 2 anbinden.

Das gleiche Problem hast du aber auch bei anderen Operatoren.

oder der Baum allgemein direkt anders erstellt werden.

Der Baum sollte anders aussehen, wenn du den "-"-Operator so interpretieren möchtest.

Das "+" muss über dem "-" sein im Baum.

Ich hoffe du verstehst was ich meine. Muss gleich Weg, sonst würde ich dir das noch aufzeichnen. ^^

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

46

19.06.2014, 20:17

Wie der Baum aussehen soll ist mir klar, nur bin ich zu doof dort hinzukommen.

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

47

19.06.2014, 23:20

Okay, ich habs.
Ich glaube ich stell mich manchmal blöd an ;) Aber ich hab mir das Problem mal isoliert betrachtet und gelöst: arithmetischer Parser

Problem ist zwar jetzt noch dass ich keine weiteren Variablen einlesen kann, aber das ist ja kleinkram.
Danke für die Hilfe, hat auf jeden Fall noch mal geholfen. Ich glaube ich muss häufiger so vorgehen :)

Und übrigens kann ich so auch direkt komplexere Ausdrücke parsen, z.B. _x = (5+5)*2-100/10...

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

48

20.06.2014, 10:37

Dein Problem ist dass deine Grammatik nicht eindeutig ist.

Quellcode

1
S -> S - S | 1

Das ist auch so ein Beispiel. Nimm mal an du willst folgenden String parsen:

Quellcode

1
1 - 1 - 1

Du kannst daraus zwei Syntaxbäume ableiten.

Quellcode

1
2
3
4
5
6
7
8
   -
1     -
    1    1

und
      -
   -     1
1    1

Bei der Implementierung wirst du dich für eine Variante entscheiden müssen. Möglicherweise sogar unbewusst. Je nach Operator macht das aber einen Unterschied. In deinem Fall ist das grad so ein Problem. Deswegen meinte ich du sollst dir mal die verschiedenen Ableitungsbäume aufmalen. Guck möglichst dass deine Grammatik eindeutig ist, dann verhinderst du solche Probleme.
„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.“

Architekt

Community-Fossil

Beiträge: 2 481

Wohnort: Hamburg

Beruf: Student

  • Private Nachricht senden

49

20.06.2014, 12:53

Hat Ähnlichkeiten mit meinem Kram. :) Hat also geholfen? ;)
Der einfachste Weg eine Kopie zu entfernen ist sie zu löschen.
- Stephan Schmidt -

DeKugelschieber

Community-Fossil

  • »DeKugelschieber« ist der Autor dieses Themas

Beiträge: 2 641

Wohnort: Rheda-Wiedenbrück

Beruf: Software-Entwickler

  • Private Nachricht senden

50

20.06.2014, 23:49

Ja etwas. Durch die Details bin ich nicht ganz durchgestiegen, dafür ist der Code doch etwas schwer zu lesen (weil komplex, nicht weil unsauber ;)).
Aber durch die Struktur konnte ich mir zumindest mal klar machen dass man bei dem kleinsten Nenner (Werte/Variablen) anfangen sollte, also alles was sich nicht weiter zerlegen lässt. Und von da an dann wieder rekursiv zurück geht.

@Schorsch: ja das war mir soweit klar, vor allem da ich kein extra Terminal Symbol verwende.
Allerdings hab ichs jetzt erstmal (ich hoffe das kann so bleiben :P) so gelöst dass nach dem ersten "parsebarem" Zeichen gesucht wird. Wenn das dann durch ist wird weiter gemacht.
Der Syntax Tree ist dann nicht nur einer, sondern mehrere kleine hintereinander.

Beispiel:

Quellcode

1
2
_x = 5 # findet =, parsed eine Zuweisung, speichert den mini tree
print _x # findet print, parsed print und speichert wieder

Werbeanzeige