Forum: PC-Programmierung Programmieren mit Lex/Yacc


von AI Z. (matrix1)


Angehängte Dateien:

Lesenswert?

Hallo,
ich habe einen Compiler zur syntaktischen Analyse geschrieben, hab 
vieles hinbekommen, nun habe ich ein neues Problem am Hals komm nur 
nicht dahinter wie ich anfangen soll.Danke.

Bei einer normalen If-Anwisung, gibt mr meine Ausagbe alles aus was es 
tun soll, nur jetzt ist es einbißchen anders. Erst wenn ich einen JMP 
befehl eingegeben hatte hatte er es erkannt und hat zugleich den Label 
angegeben. Nun aber wenn z.B ich eine If anweisung angebe, und die 
Anweisung des If's zutrifft, tut er ja den Befehl aus, ansonsten (else) 
soll er eine andere Anweisung ausführen, aber bei der Ausgabe soll 
angegeben werden, if wurde benutzt, welcher operator Konstante 
Bezeichner, tut ja mein compiler auch, nun soll er die erste Anweisung 
ausgeben (vor dem else) und dann soll er direkt ausgeben, JMP zum ende 
der If Schleife, da die Anweisung erfüllt wurde und die Anweisung nach 
der else nicht nötig ist, dann erst soll er die zweite Anweisung 
ausgeben. Ich verdeutliche dies mal anhand eines Beispiels mit 
zugewiesenen Adressen:

Eingabe Adr Ausgabe

Code:

if(x==0)        10   if(x==0)
Wait 5          20   WAIT 5
else            30   JMP 50
Wait 1          40   WAIT 1
end             50   END

so ungefähr hoffe du verstehst was ich meine.

Danke Nochmals.

MfG
Ali

von Karl H. (kbuchegg)


Lesenswert?

Du vermischt schon wieder verschiedene Dinge.
Das eine ist die Erzeugung des Syntaxbaumes.
Die andere ist die Generierung der Anweisungen.

Im Syntaxbaum besteht ein if einfach nur aus 3 Kindern

    child[0]      Bedingung
    child[1]      Statement, welches den then Teil bildet
    child[2]      Statement, welches den else Teil bildet

Das ist alles.
Mehr wird beim Baumaufbau nicht gemacht. Und dafür wird lex/yacc 
benutzt. Sobald der Baum steht, ist Lex/Yacc aus dem Rennen. Der Rest 
sind dann 'nur noch' Operationen, die auf dem Baum gemacht werden.

Der Rest erfolgt dann, wenn der Baum in eine lineare Sequenz von 
Anweisungen für die Zielmaschine umgesetzt wird. Die wird so ähnlich 
aussehen, wie deine PrintTree. Allerdings komplexer, weil sie sich genau 
um zb solche Dinge zu kümmern hat.

von AI Z. (matrix1)


Lesenswert?

Hallo,
Danke für deine Antwort,
ich hatte es mir so überlegt, dass bei meiner Eingabe, dementsprechend 
die Ausgabe gegeben werden soll:

Eingabe:

if(x==0)
y=0;
else
y=1;
end;

Ausgabe:

If
  Id: x
  const: 0
    Assign to: y
      const: 0
       JMP  (soll den Sprung zum Ende des if's anzeigen)
   Assign to: y
      const: 1


also meinst du dies ist nicht so möglich wie ich es möchte, das heißt 
ohne den JMP Befehl einzugeben würde die If Anweisung nicht den JMP 
ausgeben.
Mein obiges Beispiel verdeutlicht mein Anliegen, mein problem ist, der 
JMP befehl soll erscheinen ohne ihn vorher eingeben zu müssen, da wenn 
in der If Schleife die erste Anweisung erfüllt ist soll JMP vor dem 
ELSE, soll er natürlich dann zum Ende der Schleife springen, dann wird 
die Zweite Anweisung ausgegeben.Danke.

Gruß
Ali

von Karl H. (kbuchegg)


Lesenswert?

Ali imran Zafar schrieb:

> also meinst du dies ist nicht so möglich wie ich es möchte, das heißt
> ohne den JMP Befehl einzugeben würde die If Anweisung nicht den JMP
> ausgeben.

Nein.
Warum auch.
Den JMP musst DU während des Ausgebens generieren.

Dazu musst du deine PrintTree Funktion ein wenig anders generieren.
Der Rundumschlag am Schluss, bei dem sich alle Kinder ausgeben, muss 
weg, und dafür spezisch bei jedem Statemenet gemacht werden

Dazu brauchst du dann noch vom Compiler generierte Label.
Der Teil ist aber leicht, weil dein Benutzer Beschränkungen hat, welche 
Labelnamen er wählen darf. Aus diesen Beschränkungen ergibt sich dann zb 
ein Schema, welches der Compiler für sich benutzen kann um zwischendurch 
Labels zu erzeugen, wenn er sie braucht.

von Karl H. (kbuchegg)


Lesenswert?

Warum sieht dein IF so kompliziert aus?
Beim IF gibt es nur 2 Möglichkeiten:
Entweder es kommt ein else, oder es kommt keines
1
if_stmt: IF exp stmt_seq END 
2
      { $$ = newStmtNode(IfK);
3
        $$->child[0] = $2;
4
        $$->child[1] = $3;
5
      }
6
7
    | IF exp stmt_seq ELSE stmt_seq END 
8
      { $$ = newStmtNode(IfK);
9
        $$->child[0] = $2;
10
        $$->child[1] = $3;
11
        $$->child[2] = $5;
12
      }

von AI Z. (matrix1)


Lesenswert?

Karl heinz Buchegger schrieb:

> Den JMP musst DU während des Ausgebens generieren.

Das versteh ich nicht, wie soll ich denn den JMP Befehl während des 
ausgebens generieren.

Kannst du mir das näher erläutern.Danke.

Gruß

von Karl H. (kbuchegg)


Lesenswert?

1
int  nextLabelNr = 0;
2
char nextLabel[20];
3
4
void generateLabel( char * LabelPrefix )
5
{
6
  sprintf( "!_%s_%04d", LabelPrefix, nextLabelNr );
7
  nextLabelNr++;
8
}
9
10
void printTree(TreeNode * tree )
11
{
12
  static int indentno=0;
13
  int i;
14
  char compilerLabel[20];
15
16
  indentno += 2;  
17
18
  while (tree != NULL){
19
20
    for(i=0;i<indentno;i++)
21
      printf(" ");
22
23
    if (tree->nodekind==StmtK){
24
25
      switch (tree->kind.stmt){
26
        case IfK:
27
          printf("If\n");
28
          printTree( child[0] );                    // Bedingung
29
          printTree( child[1] );                    // Then Teil
30
31
          if( child[2] != NULL ) {                  // wenn es einen else Teil gibt
32
            generateLabel( "IF" );                  // einen Labelnamen generieren lassen ...
33
            strcpy( compilerLabel, nextLabel );     // ... und merken
34
            printf( "JMP  %s\n", compilerLabel );   // dann mit einem JMP aus dem then Teil
35
                                                    // den else Teil überspringen
36
            printTree( child[2] );                  // der else Teil soll seinen Code anhängen
37
            printf( "Label: %s\n", compilerLabel ); // und das Sprungziel für den JMP in Form
38
                                                    // eines Labels einfügen
39
          }
40
          break;
41
42
43
        case RepeatK:
44
          printf("Repeat\n");
45
          break;
46
47
...
48
49
50
//
51
// Das geht so nicht mehr
52
// Die Kinder müssen bei den einzelnen Anweisungen gedruckt werden
53
// wenn sie tatsächlich gebraucht werden
54
/*
55
    for(i=0;i<4;i++){
56
      if ((tree->child[1] != NULL) 
57
     && (tree->child[2] != NULL) 
58
     && (tree->child[i] != NULL))
59
        printf("\n"); 
60
      printTree(tree->child[i]);
61
    }
62
*/
63
64
    tree = tree->sibling;
65
  }
66
  indentno -= 2;;
67
}

Dein Rundumschlag 'Alle Kinder auf Biegen und Brechen ausgeben' 
funktioniert nicht mehr. Ab jetzt musst du die bei jeder Anweisung nur 
die Kinder ausgeben, die auch tatsächlich vorhanden sind. Dies deshalb, 
weil sich während der Ausgabe der Kinder auch noch andere Anweisungen 
(wie zb der JMP bei einem IF) ergeben, die zwischendrinn kommen müssen.

von Karl H. (kbuchegg)


Lesenswert?

Das ist jetzt eine Möglichkeit, wie man das lösen kann.

Eine andere Möglichkeit (und nicht die schlechteste), wäre es, eine 
Funktion zu schreiben, die den Baum abklappert, gezielt nach IF mit 
einem ELSE Teil sucht und die entsprechenden JMP und Label (mit 
generierten Labelnamen) einfügt.

In diesem speziellen Fall, hätte man auch die entsprechende 
Baumergänzung direkt im während der yacc Phase machen können. Dazu würde 
man wieder zusätzliche Spezialfunktionen benötigen, dia an eine 
Statementsequence, deren TreeNode Pointer man hat, hinten einen JMP mit 
einem Label drann hängt, bzw. eine Funktion, die an eine 
Statementsequence ein Label drann hängt.

Möglichkeiten gibt es mehrere. Aber alle setzen voraus, dass du mit 
Bäumen erst mal nicht auf Kriegsfuss stehst. Und daran wirds wohl schon 
wieder scheitern.

von AI Z. (matrix1)


Lesenswert?

hallo,
es klappt, man du bist einfach genial, suuuuuperrrr gut das es diese 
Foren gibt habe vieeeel hier gelernt, Ich bedank mich sehr bei dir, du 
bist voll cool.Danke nochmals.

MfG
Ali

von Karl H. (kbuchegg)


Lesenswert?

Ali imran Zafar schrieb:
> hallo,
> es klappt, man du bist einfach genial

Leider nicht.

Das ist Standardverfahren für jeden, der Compilerbau gelernt hat. Und 
auch wenn dir das wie Magie vorkommt:
* Die gepostete Lösung in printTree ist nicht besonders gut
* Die Lösung ist ziemlich naheliegend.

Spätestens wenn es darum geht, die symbolischen Labelnamen durch 
Adressen zu ersetzen, wirst du merken, dass die Lösung im printTree 
sogar extrem schlecht ist.

Hätte ich blos 3 Minuten länger über das Problem nachgedacht, und diese 
printTree Modifiktion nie vorgeschlagen. Soviel aufwändiger wäre die 
direkte Baummanipulation auch nicht gewesen.

von AI Z. (matrix1)


Lesenswert?

Hallo,

OK, das ist mein nächster Schritt die LAbelnamen durch Adressen ersetzen 
na gut, muss mir dann genauer angucken.Aber danke nochmals.

Gruß

von Karl H. (kbuchegg)


Lesenswert?

Machs nochmal rückgängig.
Die printTree Modifikation ist Murx. Die kannst du so nicht lassen, da 
mach ich mich strafbar.

Machs so
1
void appendStatement( TreeNode* sequence, TreeNode* statement )
2
{
3
  //
4
  // das letzte Statement in der Sequenz suchen
5
  //
6
  while( sequence->sibling != NULL )
7
    sequence = sequence->sibling;
8
9
  //
10
  // und das neue Statement drannhängen
11
  //
12
  sequence->sibling = statement;
13
}
1
if_stmt: IF exp stmt_seq END 
2
      { $$ = newStmtNode(IfK);
3
        $$->child[0] = $2;
4
        $$->child[1] = $3;
5
      }
6
7
    | IF exp stmt_seq ELSE stmt_seq END 
8
      { TreeNode* pJmp;
9
        TreeNode* pLabel;
10
11
        $$ = newStmtNode(IfK);
12
        $$->child[0] = $2;
13
        $$->child[1] = $3;
14
        $$->child[2] = $5;
15
16
        if( $$->child[2] != NULL ) {
17
          generateLabel();                     // einen Labelnamen erzeugen lassen
18
19
          pJmp = newStmtNode(JmpK);            // einen JMP erzeugen zu diesem Label
20
          pJmp->attr.name = strdup( nextLabel );
21
22
          pLabel = newStmtNode(LabelK);        // einen Labelknoten mit dem Namen erzeugen
23
          pLabel->attr.name = strdup( nextLabel );
24
25
          appendStatement( $$->child[1], pJmp );      // der JMP kommt hinten an den then Teil
26
          appendStatement( $$->child[2], pLabel );    // und das Label kommt hinten an den else Teil
27
        }
28
      }

Auf die Art werden der JMP und das Label bereits richtig in den Baum 
eingebaut, so dass in printTree nichts mehr hinzugefügt werden muss.

von AI Z. (matrix1)


Lesenswert?

Hallo,
wo soll ich deinen Code einfügen, deine vorige version habe ich 
rückgängig gemacht, aber versteh nicht wo ich den neuen Code hinmachen 
soll.
Soll ich etwa das void statt dem void den ich habe ersetzen und 
anschließend das while ebenfalls oder wie Danke.

Mfg
Ali

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.