C++ Top-Down Parser

P

Peacemuschel

Gast
Hallo,
Ich bin noch ziemlich neu in C++ und muss als Hausaufgabe einen Top-Down Parser schreiben.
Eine Einzulesende Datei soll auf "Grammatikalische" korrektheit überprüft werden, und evtl. einen Syntax Error ausgeben.
Der Parser soll zuweisungen folgender Formen erlauben:

a: V=F(e,e);

e: V
| F(e,e)

s: a
| a s

V in {v1,v2,v3} sowie F in {f1,f2,f3}. Ich hoffe mal diese Angaben sind halbwegs verständlich.
Zuerst stell sich mir die Frage, wie ich | a s interpretieren soll, in meinem Code habe ich es Als a und s interpretiert.

Nun zu meinem Code:
Code:
#include<iostream>
#include<fstream>
using namespace std;

bool v(ifstream& i) {
  char c;
  i >> c; cout << c;
  if (c!= 'v') return false;
  i >> c; cout << c;
  if (c != '1' && c != '2' && c != '3' ) return false;
  return true;
  }

bool f(ifstream& i) {
  char c;
  i >> c; cout << c;
  if (c!= 'f') return false;
  i >> c; cout << c;
  if (c != '1' && c != '2' && c != '3' ) return false;
  return true;
  }

bool e(ifstream& i) {
  char c;
  if (v(i)) return true;
  else {
    if (!f(i)) return false;
    i >> c; cout << c;
    if (c != '(') return false;
    if (!e(i)) return false;
    i >> c; cout << c;
    if (c != ',') return false;
    if (!e(i)) return false;
    i >> c; cout << c;
    if (c != ')') return false;
    }
  return true;
  }

bool a(ifstream& i) {
  char c;
  if (!v(i)) return false;
  i >> c; cout << c;
  if (c != '=') return false;
  if (!f(i)) return false;
  i >> c; cout << c;
  if (c != '(') return false;
  if (!e(i)) return false;
  i >> c; cout << c;
  if (c != ',') return false;
  if (!e(i)) return false;
  i >> c; cout << c;
  if (c != ')') return false;
  i >> c; cout << c;
  if (c != ';') return false;
  return true;
  }

bool s(ifstream& i) {
  if (a(i)) return true;
  if (!i.eof()) s(i);
  return true;
}


int main(int argc, char* argv[]){

ifstream i(argv[1]);

if (!s(i)) cerr << "Syntax Error" << endl;
  
return 0;
}
Ich hoffe das ist ok so, da der Code etwas länger ist.

Mein Programm gibt mir nun allerdings immer einen Syntax Error aus, auch wenn ich etwas richtiges wie z.B. v1=f1(v1,v1); oder v2= f1(v1,f2(v2,v1)); einlese. Ich finde den Fehler einfach nicht.
Kann da mal jemand drüber gucken und mir helfen?

Danke,
Felix
 
Zuletzt bearbeitet: (Code aktualisiert)
Ok, also wir haben es zumindest so gelernt, dass die Terminale klein und Non-Terminale groß geschrieben werden. Und ich denke es ist auch allgemein so. Das ändert aber an dem Problem nichts.
Zu der ersten Frage:
a|as bedeutet einfach, dass entweder ein a kommt oder mehrere a's: v1=f1(E,E);v1=f2(E,E) usw.
Ergänzung ()

also soweit ich sehe, hast du bei a() "=" vergessen

und meiner Meinung nach stimmt Funktion s auch nicht. Du rufst s(i) mit dem gleichen String i rekursiv auf. Für mich sieht es nach endloser Rekursion aus. Ich kenn mich aber leider mit C++ nicht aus.
Wenn du mehrere a's in Folge abarbeiten willst, musst du nach abarbeitung von dem ersten a überprüfen, ob es noch Zeichen gibt, wenn ja rufe a() nochmal auf.

Das ist so mein erster Gedanke. Ich weiß nicht, ob die Funktion >> nächsten Char nur anschaut oder löscht. Wenn die Chars nach und nach gelöscht werden, kannst du es ausnutzen. Wenn nicht, musst du dir was einfallen lassen.

Ja und nur mit boolischen Werten zu arbeiten ist zwar nett, aber dann weißt du nicht an welcher Stelle der Fehler ist. Wenn du deine Lösung nicht stark ändern willst, kannst du jedes mal, wenn false zurückgeliefert wird, in so einen String das entsprechende Zeichen reinschreiben, bei dem Fehler aufgetretten ist. Ob es in dem Fall viel bringt, weiß ich nicht.
 
Zuletzt bearbeitet:
tshape schrieb:
Ergänzung ()

also soweit ich sehe, hast du bei a() "=" vergessen

Stimmt, das '=' habe ich vergessen. Danke!

Allerdings hab ich da wohl noch mehr Fehler eingebaut, das Problem bleibt bestehen. Dann such ich mal weiter :p

tshape schrieb:
Ich bin jetzt auch schon länger am überlegen ob es an s() liegt. Habe jetzt erstmal den Code so abgeändert, dass mir jedes c wieder ausgegeben werden sollte, allerdings erhalte ich nur Syntax Error. Ich denke, das daher die Vermutung das s() falsch ist verstärkt wird, oder ich bin zu doof Dateien einzulesen. Mit den boolischen Werten arbeite ich, weil wir das in der Vorlesung auch so gemacht haben.
 
Zuletzt bearbeitet:
Jo ansonsten gehen mir die Tipps aus :)
Die Zeile 39 musst du so lassen wie du es in deinem Code oben hast. Die Methoden sind soweit ok. Kann sein, dass ich auch was übersehen habe.
Wie gesagt, mit mehreren a's musst du es anders machen.
Mein Vorschlag:
Rekursiven Aufruf aus der Methode s() entfernen und da nur a() aufrufen. Erstmal probieren, ob es mit einem a läuft. Wenn es gut läuft, in der Methode a() ganz am Ende der Methode abfragen, ob es weitere Zeichen gibt, wenn ja dann a() rekursiv mit dem RESTSTRING aufrufen.

Hoffentlich das bringt dich weiter. Viel Erfolg.

EDIT: Ahja und in der methode e() und allgemein aufpassen, dass du bei dem rekursiven Aufruf e(i) der jeweiligen Methode nicht wieder den gesamten String übergibst, sondern immer nur den restlichen String.
Ergänzung ()

Peacemuschel schrieb:
Ich bin jetzt auch schon länger am überlegen ob es an s() liegt. Habe jetzt erstmal den Code so abgeändert, dass mir jedes c wieder ausgegeben werden sollte, allerdings erhalte ich nur Syntax Error. Ich denke, das daher die Vermutung das s() falsch ist verstärkt wird, oder ich bin zu doof Dateien einzulesen. Mit den boolischen Werten arbeite ich, weil wir das in der Vorlesung auch so gemacht haben.

Also zusammengefasst zwei Kritikpunkte:
- Rekursiven Aufruf von s() entfernen. Du kommst da in die endlose Rekursion nur deswegen nicht rein, weil a() immer nur false liefert, weil
- du die Datei in den String i einliest und JEDER Methode diesen gesamten String übergibst. Die bereits gelesenen Zeichen müssen aber entfernt werden. In jeder Methode wird die gesamte Datei von vorn gelesen. Ich kenn mich aber wie gesagt mit C++ nicht aus, deswegen bin ich mir hier nicht sicher.

Mach doch in jeder Methode so eine Consolen-Ausgabe des Strings, der gerade gelesen wird, dann wirst du sehen, was in den Methoden passiert.
 
Zuletzt bearbeitet:
Zeile 10:

if (c!=(1 or 2 or 3)) return false;

Du meinst wohl: if (c != '1' && c != '2' && c != '3' ) return false;


Whitespaces: Vielleicht enthält dein Eingabestrom Tabulatoren, Leerzeichen oder Zeilenende. Das könnte Probleme bereiten, da du das ja nicht prüfst.
 
r0b0t schrieb:
Zeile 10:

if (c!=(1 or 2 or 3)) return false;

Du meinst wohl: if (c != '1' && c != '2' && c != '3' ) return false;


Whitespaces: Vielleicht enthält dein Eingabestrom Tabulatoren, Leerzeichen oder Zeilenende. Das könnte Probleme bereiten, da du das ja nicht prüfst.

Danke! Das hat mir schon einiges gebracht.
Desweiteren hat:
Code:
i >> c;
if (!e(i)) return false;
Für Fehler gesorgt, ich darf vor solchen Überprüfungen keinen neuen Char einlesen.
Nun den Code abändern und hoffen das alles klappt.
Danke Für eure Hilfe!

Edit: So das Problem bleibt bei den rekursiven Funktionen.
Code:
bool e(ifstream& i) {
char c;
if (v(i)) return true;
else {
if (!f(i)) return false;
...
Meine Vermutung:
Wenn ich v(i) aufgerufen habe ist i schon 2 Zeichen weiter, benötigen tu ich allerdings bei (!f(i)) i0, also das gleiche i das auch bei v(i) genutzt wird.
Ergänzung ()

Ich habe den Code oben mal soweit aktualisiert. Allerdings stehe ich immer noch vor dem Problem das die Rekursion bei bool e nicht klappt. Hat jemand eine Idee wie ich das ganze angehen könnte?
 
Zuletzt bearbeitet:
Zurück
Oben