Hallo.
hab seit kurzer Zeit angefamgen mit Flex/Bison zu arbeiten. hab ne
Aufgabe leider hänge ich an einer Stelle vielleicht kann mir jemand
weiterhelfen.
wenn man eine eine Eingabe eingibt z.B: so sieht die aus:
:L1
IF FLAG AND X"0001"
EVT 23;
ELSE
WAIT 500 ms;
JMP L1;
END IF;
so soll der compiler jedes einzelne Element erkennen und wiedergeben,
z.B. Es wurde ein If benutzt eine const, ein Identifier ein else,
when.... und der Befehl JMP. Hab soweit hinbekommen, dass der compiler
alle Eingaben erkennt, nur weiß ich nicht wie der JMP erkannt werden
soll. JMP ist ein Jump Befehl wie bei Assembler, also falls JMP in der
Standardeingabe eingegeben wird, dann soll ausgegeben werden, keyword:
JMP, Assign to: L1, da in meinem Beispielcode JMP zu L1 springt. Hab
rausgefunden, dass man in dem Baum nach dem entsprechenden Ziel-Label
von dem JMP Befehl suchen muss oder man legt sich gleich eine Tabelle
an, wo man einem Label gleich einen Knoten im Baum zuordnen kannst.
Leider komme ich nicht darauf, wie soll ich eine Tabelle anlegen oder
wie frage ich ab dass ein JMP eingegeben wurde und er soll danach
suchen. erst dachte ich da gibt es die goto funktion aber komm leider
nicht auf die Idee.Danke für euere Hilfe.
Ich häng mal den Code an.
Hier das Syntaxtree.h file:
typedef enum {StmtK, ExpK} NodeKind;
typedef enum {IfK, RepeatK, AssignK, ReadK, WriteK, ForK, WhileK}
StmtKind;
typedef enum {OpK, ConstK, IdK} ExpKind;
typedef struct treeNode{
struct treeNode * child[4];
struct treeNode * sibling;
NodeKind nodekind;
union {StmtKind stmt; ExpKind exp;} kind;
union { char * op;
int val;
char * name;} attr;
} TreeNode;
MfG
Zafar
Bei dir erzeugt ja jedes Statement einen TreeNode. Dein ganzes Programm
ist daher im Grunde nichts anderes als ein Baum, wobei ein Baumknoten
ein TreeNode ist.
Bei einem Label erzeugst du dir daher einen speziellen TreeNode, so wie
du das für ein for oder ein while auch gemacht hast. Ein TreeNode für
ein Label. Der Knoten selbst macht nichts, sondern dient nur als
Platzhalter, so dass du innerhalb des Baumes eine Möglichkeit hast,
einen Knoten wiederzufinden. Du willst den Namen des Labels in diesem
Knoten speichern, denn das ist dein Suchkriterium, mit dem du den Knoten
im Baum wiederfindest.
Bei einem JUMP machst du im ersten Durchlauf nichts. Du speicherst beim
Jump-TreeNode einfach nur den Namen, welches Label angesprungen werden
soll.
Die eigentliche 'Magie' des Jump passiert in einem 2-ten Durchlauf durch
den Baum. In diesem Durchlauf gehst du den Baum komplett durch und
bearbeitest jeden JUMP den du findest. Beim JUMP-TreeNode steht ja noch
das Label dabei, wo hingesprungen werden soll. Mit diesem Namen gehst du
in eine Suchroutine, die im Baum einen Label-TreeNode mit genau diesen
Namen sucht. Findet die Suchroutine den entsprechenden Label-TreeNode so
liefert sie dessen Adresse zurück. Diese Adresse wird dann beim Jump
eingetragen (nach entsprechenden Prüfungen selbstverständlich).
Ein Jump ist also nichts anderes als ein TreeNode mit einem Pointer,
wobei der Pointer auf einen anderen TreeNode im Baum zeigt.
mit Lex und Yacc hat das ganze erst mal nichts zu tun. Das ist ganz
normale Baum-Behandlung.
Und klar. Man kann sich natürlich auch noch ein Array aufbauen, in dem
die Labelnamen als Index dienen und man die Adresse des zugehörigen
TreeNodes direkt ablegt. Dann vereinfacht sich die Suche. Allerdings: Da
JUMP in einem Programm nicht allzuoft vorkommt, spielt der
Geschwindigkeitsvorteil in der Praxis wohl kaum eine grosse Rolle. Ob
der zusätzliche Aufwand für diese Tabelle den Laufzeitgewinn aufwiegt,
darf für übliche Programme bezweifelt werden.
sein. Ich denke nicht, dass es schlau ist, sich einen Pointer in die
Bison interne Stringverwaltung zu merken. Aber ich bin da ehrlich gesagt
auch nicht so bewandert, wie das funktioniert.
Hallo,
vielen dank für deine Ausführliche Antwort, hab jetzt auch ein treeNode
wie du gesagt hast für label und jmp gemacht, ist das etwa so in
ordnung:
label_stmt: LABEL '(':' 'NAME')' stmt_seq END
{ $$ = newStmtNode(LabelK);
$$->child[0] = $3;
}
jmp_stmt: JMP '('label_stmt')' stmt_seq END
{$$ = newStmtNode(JmpK);
$$->child[0] = $3;
$$->child[1] = $5;
}
Danke.
MfG
zafar
Ali imran Zafar schrieb:> label_stmt: LABEL '(':' 'NAME')' stmt_seq END> { $$ = newStmtNode(LabelK);> $$->child[0] = $3;>> }
wieso auf einmal '(' und ')'
willst du schreiben
L1(:)
oder willst du schreiben
:L1
wieso gibt es da ein LABEL und ein NAME?
und was soll die stmt_seq da sein?
> jmp_stmt: JMP '('label_stmt')' stmt_seq END> {$$ = newStmtNode(JmpK);> $$->child[0] = $3;> $$->child[1] = $5;> }
Das denke ich nicht.
Bei einem Jump kannst du dir beim Parsen nur merken, wie das Label
heisst, welches es anzuspringen gilt.
Auch stimmt die Syntax nicht. Im Programm soll doch einfach nur stehen
JMP L1;
Deine Syntax, die du da dem Bison unterjubeln willst, verlangt aber
etwas ganz anderes.
(mich beschleicht schön langsam das Gefühl, dass du den am Anfang
geposteten Parser gar nicht selbst geschrieben, sondern geklaut hast und
in Wirklichkeit nicht so recht vestehst, was da eigentlich passiert)
Aus Sicht deines Parsers ist ein Label ganz einfach ein Statement
(welches nicht mit einem ';' abgeschlossen ist) welches aus einem : und
einer darauffolgenden Zeichenkette besteht.
Aus Sicht deines Parsers ist ein JUMP ganz einfach ein Statement,
welches aus dem Schlüsselwort JUMP und einer darauffolgenden
Zeichenkette besteht (und dem obligatorischen ';', aber der wird mit
einer anderen Regel gefordert)
Mehr steckt da nicht dahinter. Und natürlich noch, das für die
Zeichenketten spezielle Regeln existieren, so wie ein Variablenname ja
uch nicht einfach nur eine Zeichenkette ist, sondern gewissen Regeln
unterliegt.
Hallo,
Also wenns JMP L1; heißen soll dann sollte es doch so aussehen:
jmp_stmt: JMP '('NAME',';')' stmt_seq END
{$$ = newStmtNode(JmpK);
$$->child[0] = $3;
$$->child[1] = $5;
}
Gruß
Ali imran Zafar schrieb:> jmp_stmt: JMP '('NAME',';')' stmt_seq END
Das hier sagt:
Eine Jump Anweisung besteht aus
dem Schlüsselwort JMP
gefolgt vom Zeichen '('
gefolgt von einem Namen
gefolgt von Zeichen ','
gefolgt von einem Syntax Error (was soll der ; da sein?)
gefolgt vom Zeichen ')'
gefolgt von einer Anweisungskette
gefolgt vom Schlüsselwort END
JMP L1; erfüllt diese Bedingungen niemals!
Ein Jump Statement soll so aussehen
JMP L1;
und genau das forderst du auch
jmp_stmt: JMP NAME
ein JMP Statement ist etwas, das mit dem Schlüsselwort JMP beginnt und
danach kommt noch ein NAME. Fertig. Das ist ein JMP Statement.
Wenn du die Syntaxregel festlegen willst, dann musst du dich daran
orientieren, wie du letztendes in der zu erstellenden Programmiersprache
schreiben willst. Und das formulierst du dann als Regel. In welcher
Reihenfolge muss was kommen, damit das alles in Summe das Geforderte
ergibt
Wenn deine Sprache eine Anweisung haben soll
SCHNTZLBRTZ { 8 () };
dann forderst du
zuerst muss da SCHNTZLBRTZ stehen
nach dem Schlüsselwort muss eine { kommen
danach eine Zahl
gefolgt von (
gefolgt von )
und als Abschluss noch }
und das schreibst du genau so hin
schntzrtz_stm = SCHNTZLBRTZ '{' NUM '(' ')' '}'
Einfach die Aufzählung, was in welcher Reihenfolge da stehen muss, damit
das alles in Summe ein schntzrtz_stm ergibt.
hallo,
dann sollte mein Label auch so aussehen:
label_stmt: ':' NAME
{ $$ = newStmtNode(LabelK);
$$->child[0] = $3;
}
jmp_stmt: JMP NAME
{$$ = newStmtNode(JmpK);
$$->child[0] = $3;
$$->child[1] = $5;
}
Ich habe erst seit kurzem angefangen mich mit flex/bison zu
beschäftigen, bin sozusagen ein Anfänger auf diesem Gebiet, deshalb habe
ich nicht soviel Ahnung, aber ich lern mit der Zeit. Danke für deine
Hilfe.
Wenn ich den Namen des Labels ausgeben will was mache ich dann, in der
switch anweisung im .y file, muss ich da:
case LabelK:
printf(" (hier NAME oder was soll hier rein) \n");
break;
Danke.
Gruß
zafar
Ali imran Zafar schrieb:> Ich habe erst seit kurzem angefangen mich mit flex/bison zu> beschäftigen,
Dann solltest du mit einfacheren Dingen als einer kompletten Sprache
anfangen. Es gibt genügend Tutorials, die einem das auch ohne dem Besuch
der Vorlesung 'Formale Sprachen' auf der Uni beibringen.
Ja gut ich habe schon etliche Tutorials gelesen angewendet, kleine
Programme geschrieben, nun ist zeit, dass ich mich den größeren Sachen
widme, aber trotzdem kann man ja nicht alles wissen, deshalb gibt es ja
die Foren, hänge schon lange an dieser JMP anweisung, deshalb brächte
ich Hilfe beid er Sache.Aber Danke nochmal hast mir viel geholfen, hab
auch einiges mehr Verstanden.
Gruß
Ali
Ali imran Zafar schrieb:> aber trotzdem kann man ja nicht alles wissen
Na ja.
Das sind aber eigentlich Grundlagen:
Die Beschreibung eines Musters, welches in der Eingabe vorliegen muss,
damit dieser Teil der Eingabe sich als irgendetwas qualifiziert.
Und um mehr geht es zur Zeit noch nicht:
Wie soll mein Eingabetext aussehen?
Welches Muster muss daher der Eingabetext haben?
Dieses Muster in Bison Syntax formulieren.
Hallo,
wollt mal fragen ob du mir erklären könntest was die Anweisunge:
for_stmt: FOR '(' assign_stmt ',' exp ',' assign_stmt ')' stmt_seq END
{ $$ = newStmtNode(ForK);
$$->child[0] = $3;
$$->child[1] = $5;
$$->child[2] = $7;
$$->child[3] = $9;
genau zu bedeuten hat, die erste Zeile hast mir ja in details erklärt,
fand ich gur kannst mir erklären was die nächsten Zeilen bedeuten. hab
diesen Code im Internet gefunden gehabt und passt sehr gut zu meiner
Arbeit wäre gut wenn ich dsa hier auch einbißchen verstehen würde.Danke.
Gruß
Ali
Ali imran Zafar schrieb:
Ich denke du solltest noch mal deine Tutorien studieren (*), was es mit
diesen $-Zahlen auf sich hat.
Und für den Rest: Ich mag es kaum glauben, dass sich jemand an
Compilerbau versucht, aber mit stink normalen dynamischen
Datenstrukturen wie Bäumen auf Kriegsfuss steht. Das legt einen Knoten
für den Baum an und befüllt ihn mit Werten. Welche Werte? Nun, den Werte
die den variablen Teilen in der Musterbeschreibung entsprechen und die
Bison besorgt hat, als er beschlossen hat, dass eine Eingabezeile dem
Muster eines for_stmt entspricht.
Das Muster besteht aus mehreren Teilen. Einige davon sind fix, andere
sind variabel.
for_stmt: FOR '(' assign_stmt ',' exp ',' assign_stmt ')' stmt_seq END
1 2 3 4 5 6 7 8 9
und jetzt vergleich mal
$$->child[0] = $3;
$$->child[1] = $5;
$$->child[2] = $7;
$$->child[3] = $9;
Da werden offenbar Pointer gespeichert (weil child ein Array von Pointer
ist). In child[0] kommt ein Pointer auf das 3-te Identifizierte. Was ist
das? Nun schau dir die Regel an. Der 3te Teil ist assign_stmt. Wenn das
ganze ein for_stmt ist, dann muss auch assign_stmt (siehe dessen Regel)
einen Pointer auf einen Knoten geliefert haben (bei diesem Knoten
beginnt der Baumaufbau für diese Zuweidung). Dieser Pointer wird in
child[0] gespeichert.
D.h. ein 'for-Anweisung' wird in einen Baum-Knoten umgesetzt. Dieser
Baumknoten enthält 4 Pointer.
Der erste zeigt auf einen Zuweisungsknoten (der selbst wieder Kinder
haben wird). Dieser Baumteil beschreibt die Zuweisung, die als erstes im
for abgearbeitet werden soll.
Der zweite Pointer im for zeigt auf einen Knoten für eine Expression.
Diese Expresion wertet die Schleifenabbruchbedingung aus.
Der dritte Pointer im for zeigt wieder auf eine Zuweisung. Offenbar die
Zuweisung, die bei jeder Schleifeniteration durchgeführt werden soll
Und der vierte Pointer im for zeigt offenbar auf die Anweisung, die im
Schleifenrumpf ausgeführt werden soll.
Vor mich sieht es momentan so aus, als ob du einen viel zu großen
Brocken zu schlucken versuchst.
(*) studieren!
Nicht Abschreiben oder per Copy/Paste in deine Entwicklungsumgebung
holen, laufen lassen, sich zurücklehnen und 'schön' sagen.
Studieren bedeutet: verstehen was im Tutorium gezeigt wird, eigene
Versuche machen, Zusammenhänge erkennen, das verstanden geglaubte mit
eigenen Tests überprüfen.
Sag mal Karl Heinz, von was hast Du eigentlich KEINE Ahnung?
#Schwer beeindruckt.
Hab von Yaks, Bisons und den anderen Rindviehchern zum letzten mal im
Studium gehört...
Tom
Thomas Burkhart schrieb:> Sag mal Karl Heinz, von was hast Du eigentlich KEINE Ahnung?
Alles was mit KI zu tun hat.
Neuronale Netze
Buchhaltungsprogramme :-)
...
> #Schwer beeindruckt.
"Compilerbau", "Formale Sprachen" waren meine Lieblingsvorlesungen auf
der Uni und ich mag diesen Bereich auch heute noch sehr gerne. Auch wenn
ich Lex/Yacc (Bison) nicht besonders gerne benutze. Ich mag rekursiven
Abstieg lieber. Ist einfacher zu debuggen, warum die Sprachbeschreibung
nicht so funktioniert wie vorgesehen.
> Der erste zeigt auf einen Zuweisungsknoten (der selbst wieder> Kinder haben wird). Dieser Baumteil beschreibt die Zuweisung,> die als erstes im for abgearbeitet werden soll.> Der zweite Pointer im for zeigt auf einen Knoten für eine> Expression. Diese Expresion wertet die Schleifenabbruchbedingung> aus.> Der dritte Pointer im for zeigt wieder auf eine Zuweisung.> Offenbar die Zuweisung, die bei jeder Schleifeniteration> durchgeführt werden soll> Und der vierte Pointer im for zeigt offenbar auf die Anweisung,> die im Schleifenrumpf ausgeführt werden soll.
Etwas von der Form
Hallo,
Ich danke dir sehr, das hat mir wirklich sehr geholfen. Ich habe noch
nie mit Flex/Bison gearbeitet, und c++ hatte ich nur gering im Studium
ich bin eher der Hardware spezialist und nicht der
Programmierspezialist, aber hab nun eine Aufgabe bekommen, muss nun
programmieren, aber ich glaube ich sollte nochmal paar tutorials
ansehen, ehrlich gesagt habe ich in keinem Tutorial was von den $
Zeichen gesehen gehabt. ist nicht nur so dass ich einfach schnell meine
Arbeit abarbeiten will, will ja auch verstehen was da gemacht wird,
deshalb frage ich ich auch an und danke, hab auch die entsprechende
Antwort erhalten.
Gruß
Ali
Thomas Burkhart schrieb:> Du machst sowas aber nicht von Hand oder??
Doch :-)
Mit der Zeit kriegt man Übung und es geht schnell.
Ich kann nur jedem raten:
Wer mit dynamischen Datenstrukturen zu tun hat und noch nicht so die
Übung da drinnen hat: Zeichnet mit!
Natürlich nicht mit ASCII Graphik, so wie ich hier.
Papier, ein paar unterschiedliche Buntstifte und ein Radiergummi sind
völlig ausreichend.
So eine Zeichnung ist ein exzellentes Debugwerkzeug. Das steckt jeden
Debugger aber locker in die Tasche, wenn es um die Aspekte: "Wo greife
ich eigentlich zu, stimmt mein Strukturaufbau, ist die
Manipulationsoperation in Ordnung oder fehlerhaft" geht.
Ali imran Zafar schrieb:> programmieren, aber ich glaube ich sollte nochmal paar tutorials> ansehen, ehrlich gesagt habe ich in keinem Tutorial was von den $> Zeichen gesehen gehabt.
Dann kann's kein Tut über yacc/bison gewesen sein.
Hallo,
Hab nun so für label und JMP geschrieben, bin aber einbißchen
durcheinander, versteh nicht mehr was was sein soll, kannst du mir bitte
hier weiterhelfen.Danke.
label_stmt: ':' NAME
{ $$ = newStmtNode(LabelK);
/*$$->child[0] = $2;*/
$$->attr.s = strdup($2);
}
jmp_stmt: JMP NAME
{$$ = newStmtNode(JmpK);
/*$$->child[0] = $1;*/
/*$$->attr.s = strdup($2);*/
$$->child[1] = $2;
}
Ali imran Zafar schrieb:> jmp_stmt: JMP NAME> {$$ = newStmtNode(JmpK);> /*$$->child[0] = $1;*/> /*$$->attr.s = strdup($2);*/> $$->child[1] = $2;> }
Das kann nicht sein.
Zum Zeitpunkt des Compilierens kennst du die Codestelle noch nicht, wo
der Sprung hingehen soll. Du kennst nur den Namen des Labels.
Hallo,
(gibt es hier eine Möglichkeit, wie es bei Assembler es gibt, Adressen
anzufragen, z.B. könnte ich sagen, wenn JMP befehl kommt springe zu der
oder der Adresse, das wär die Adresse wo sich label_stmt: ':' L1
befindet
Danke.)
Hab herausgefunden, dass es ein Stack Machine gibt, dazu gibt es
verschiedene Register, IR (Instruction Register), PC (Program Adress
Register), AR (Activation Record Register), T (stack Top register)
dann könnte ich doch den Top Register nehmen, welches die Adresse des
Top elements des stacks hat.
Gruß
Ali
Ali imran Zafar schrieb:> Hallo,>> (gibt es hier eine Möglichkeit, wie es bei Assembler es gibt, Adressen> anzufragen, z.B. könnte ich sagen, wenn JMP befehl kommt springe zu der> oder der Adresse, das wär die Adresse wo sich label_stmt: ':' L1> befindet
Du verwechselst du etwas.
Im Moment geht es darum den Baum aufzubauen.
Es geht noch nicht darum den JMP auch tatsächlich abzuarbeiten.
Und nein. Zum Zeitpunkt der Syntaxanalyse kennst du das Sprungziel nur
dem Namen nach. Es kann ja auch erst weiter hinten im Code kommen, der
nach gar nicht analysiert wurde.
>> Hab herausgefunden, dass es ein Stack Machine gibt, dazu gibt es> verschiedene Register, IR (Instruction Register), PC (Program Adress> Register), AR (Activation Record Register), T (stack Top register)> dann könnte ich doch den Top Register nehmen, welches die Adresse des> Top elements des stacks hat.
Zum jetzigen Zeitpunkt alles uninteressant.
Bis du zur Statemachine kommst vergeht noch vieel Zeit. Im Moment ist
die Syntaxanalyse drann, die eine Baumrepräsentierung des
Eingabeprogramms erzeugt, Syntaxfehler meldet. Mehr nicht.
Danach schliesst sich dann die Phase an, in der dieser Baum auf Fehler
abgeklopft wird, wie zb Sprungziele auf Labels, die gar nicht
existieren.
Dann kommt die Phase in der die Variablen gesammelt werden und Adressen
zugewiesen bekommen (je nach Datentyp).
Danach die Phase, in der die arithmetischen Ausdrücke im Baum aufgesucht
werden und (je nachdem welche Datentypen die Sprache hat, wenn sie
überhaupt welche hat) neue Knoten zur Datentypanpassung und Wandlung
eingefügt werden (so nicht die Stackmaschine Instruktionen aufweist, die
diese Anpassungen erledigen)
Daran schliesst sich die Phase an, in der einfache Optimierungen in
diesem Baum gemacht werden, wie zb bestimmte Sequenzen durch andere zu
ersetzen bzw. konstante Ausdrücke vorab auszurechnen und damit Teilbäume
durch Konstanten-Knoten zu ersetzen.
(*)
Und erst dann .... wird aus der Baumrepräsentierung eine Liste von
Anweisungen für die Stackmaschine generiert (oder die Stackmaschine
direkt aus dem Baum heraus gefüttert). Aber so wie es aussieht, wirst du
deine Stackmaschine nicht direkt füttern, sondern ein 'Programm' dafür
erzeugen müssen - quasi das Assemblerprogramm, welches aus dem Baum
entsteht und das diese spezielle Stackmaschine versteht. Und während
dieser Generierung fällt natürlich die 'Adresse' der Instruktion ab, die
einem Hochspachenlabel entspricht und welche dann bei allen JMP
substituiert werden kann.
Und ehe du deinen Compiler Instruktionen für diese spezielle
Stackmaschine generieren lässt, ist es mehr als ratsam selbst einmal ein
paar Programme in der Maschinensprache dieser Stackmaschine zu
schreiben. Das vermittelt dann das Wissen und auch ein gewisses Gefühl
dafür, was der Compiler eigentlich bei bestimmten Dingen erzeugen muss,
damit das generierte Assemblerprogramm auch wirklich dem
Hochsprachenprogramm entspricht.
Oder so ausgedrückt: Ein Deutsch-Englisch Dolmetsch wird nicht umhin
kommen, selbst Englisch sprechen zu können. Wer hingegen einen
Deutsch-Englisch Dolmetsch ausbilden will (und genau in der Situation
bist du als Compilerbauer) sollte besser fliessend Englisch sprechen.
Das er mit dem Deutschen auch nicht auf Kriegsfuss stehen darf, versteht
sich von selbst.
Edit: (*)
Und wahrscheinlich habe ich noch mindestens ein halbes Dutzend Passes
durch den Baum übersehen, die aber notwendig sind, ehe die
Codegenerierung für die Statemachine auch nur ansatzweise beginnen kann.
Was ist zb mit Funktionsaufrufen in der Sprache? Was ist mit
'eingebauten' Funktionen wie 'sqrt', 'sin', 'cos', etc.
All das muss behandelt werden bzw. es muss zumindest eine Vorstellung
davon geben, wie man das behandeln will.
Wer auch immer dir diese Aufgabe gegeben hat:
Schimpfe ihn einen totalen Volltrottel und gib die Aufgabe zurück.
Auch mit Lex/Bison kannst du nicht ein komplettes Vorlesungssemester in
ein paar Tagen aufholen, in der du systematisch in die Kunst des
Compilerbaus eingewiesen und mit praktischen Beispielen und Übungen
begleitet wirst.
Was du da machst ist komplett sinnlos. Dir fehlt viel zu viel, als das
es Sinn macht, da weiterzumachen.
Da du in Hardware bewandert bist:
Das ist als ob man von dir verlangen würde, den Frequenzgang in einem
Verstärker zu begradigen, und du fängst an mit: Ich hab schon mal nach
'Ohmschen Gesetz' gegoogelt und ich glaub ich habs auch verstanden. Aber
wie wende ich das jetzt auf meinen Verstärker an?
Hallo,
ja du hast eigentich schon recht, in so kurzer zeit kann ich auch nicht
all das verstehen. Aber um meine Aufgabe zu beenden muss ich nur noch
diesen einen JMP befehl genereieren, sonst funktioniert der Rest, kannst
du mir bitte nicht dabei weiterhelfen dies zu ende zu bringen, ich
glaube ich bin an einem Punkt angelangt, wo ich nicht weiter kommen
werde, da wie du schon gesagt hast, dass wissen fehlt, es wäre super,
wenn du mir hier weiterhelfen könntest.Danke.
Gruß
Ali
Ali imran Zafar schrieb:> diesen einen JMP befehl genereieren, sonst funktioniert der Rest, kannst> du mir bitte nicht dabei weiterhelfen dies zu ende zu bringen,
Das interessante dabei ist:
Ich hab dir schon mindestens 3 mal gesagt, was es bei einem JMP während
des Syntaxscannes zu tun gibt. Einfach den Namen des Labels, welches
angesprungen werden soll, wegspeichern. Dazu hast du im TreeNode ein
schänes name Feld, welches sich dafür anbietet.
Ich denke, das waren genügend Hinweise. Noch deutlicher kann ich gar
nicht sein, aussser das ich schreibe, ein JMP wird in dieser Phase des
Compilers so behandelt, da das das Einzige ist, was du zum jetzigen
Zeitpunkt (also während das Syntaxscannens) tun kannst:
jmp_stmt: JMP NAME
{$$ = newStmtNode(JmpK);
$$->attr.name = strdup($2);
}
aber das Schreib ich ja nicht.
(Und bei einem Label spricht auch nichts dagegen, den Labelnamen im name
Feld abzulegen und nicht in s. Bei
:L1
kann man L1 durchaus als 'den Namen des Labels' auffassen.)
@karl-heinz
Danke für das erneute Auffrischen meiner Compilerbau-Vorlesung von vor
12 Jahren :-)) Ich hatte zwar (soweit meine verstaubten Zellen noch
irgendwelche Zuckungen an Erinnerungen dieser Zeit von sich geben) nur
den Syntaktischen Teil gehabt (also nur Tokenizer und Parser, aber auch
mit Tools wie Bison/Lex) aber war davon sehr angetan und versuche mir
seit dem immer mal wieder nen Compiler zu schreiben :-))
Ich denke mittlerweile habe ich dazu schon genügend Kenntnisse (selbst
wenn ich keine Tools wie Bison/Yacc/Lex/usw verwende).
Da mal direkt eine Frage zu ...
Gibt es ein Beispiel *.l und *.y File in dem die Struktur der Sprache C
komplett enthalten ist ? (ich brauche nur die syntaktische Auflistung)
Also so ähnliche Statements wie Oben mit "Name = Aufbau END {
Wegspeichern } ...". In EBNF müsst ich mich auch erst wieder
einarbeiten.
Und wenn es so beschrieben ist wie oben ist es einfacher lesbar.
Ich habe ja (mehrere) einfache Parser geschrieben, und als eines der
erklärten Ziele möchte ich zum einen einen Interpreter schreiben (auf
Basis meines Parsers, evtl mit Byte-Zwischencode) und zum anderen möchte
ich mich nun endlich mal an einen C-Compiler wagen. Selbst wenn ich
erstmal "nur" den Text syntaktisch überprüfe und mir eine Baumstruktur
daraus generieren lasse um mir dann Informationen über das Programm zu
generieren (welche Funktion ist in welchem Modul, usw)
Aber dazu müsste ich die komplette Syntax (am liebsten dann in der
Bison/Lex-Schreibweise) haben um Teile dann davon mit meinem Parser
umsetzen zu können.
Ist vllt ein bissl gewagt bzw hoch gesteckt, aber wie heißt es so schön
: "Meet the challenge" :-)).
Und ich denke vom Prinzip her müsste sich das so machen lassen wie ich
mir das Vorstelle mit meinem Parser.
Rene Böllhoff schrieb:> Da mal direkt eine Frage zu ...> Gibt es ein Beispiel *.l und *.y File in dem die Struktur der Sprache C> komplett enthalten ist ?
Müsste es irgendwo im Web geben.
Wenn nicht, hol dir die gcc-Sourcen (oder einen anderen freien
Compiler). Ich denke, die benutzen Lex/Yacc.
Das Problem an der C-Syntax: Sie ist nicht LL1 und die Umformung in LL1
ist auc nicht wirklich trivial. D.h. mit nur einem Token Vorschau kann
man nicht in allen Fällen entscheiden, wie es weitergeht. Damit ist ein
gewöhnlicher rekursiver Abstieg so ohne weiteres nicht möglich. Der Yacc
kommt aber out of the Box damit klar.
Google Stichwörter wären IMHO
C grammer yacc
Damit, oder Variationen davon, sollte sich was finden lassen.
Bingo:
erster Treffer
http://www.lysator.liu.se/c/ANSI-C-grammar-y.html
(ok, 1985 ist schon alt)
Der hier sieht auch gut aus:
http://www.thefreecountry.com/sourcecode/grammars.shtml
Karl heinz Buchegger schrieb:> Wenn nicht, hol dir die gcc-Sourcen
Der nicht. Früher hatte GCC mal Bison verwendet, aber seit GCC wirklich
brauchbare Fehlermeldungen bei syntaktischen Fehlern auswirft tut er das
nicht mehr.
@A.K. und Karl Heinz
Erstmal danke für die Links und Infos. Das das ganze nicht so ganz
trivial ist ist mir klar :-)). Geht sich auch erstmal nur um einen
Versuch ob ich das mit meinem Parser gestemmt bekomme. Dadurch das sich
der Parser sehr leicht rekursiv gestalten lässt sollte man damit was
Programmiersprachen angeht einige Möglichkeiten haben. Daher auch die
Erwähnung des Interpreters. Bei einer entsprechen einfach gehaltenen
Programmiersprache müsste sich sowas mit dem Parser (nur erstmal die
syntaktische Seite) an ein paar Nachmittagen realisieren lassen.
A. K. schrieb:> Karl heinz Buchegger schrieb:>>> Wenn nicht, hol dir die gcc-Sourcen>> Der nicht. Früher hatte GCC mal Bison verwendet, aber seit GCC wirklich> brauchbare Fehlermeldungen bei syntaktischen Fehlern auswirft tut er das> nicht mehr.
Ah. ok. Danke
Hab mir die gcc Sourcen vor 20-jahren mal geholt, reingeguckt,
entschieden dass ich keinen blassen Schimmer habe, wie das funktioniert
und ... runtergelöscht.
Ja, ja. Das leidige Thema Fehlermeldungen.
Rene Böllhoff schrieb:> Erwähnung des Interpreters. Bei einer entsprechen einfach gehaltenen> Programmiersprache müsste sich sowas mit dem Parser (nur erstmal die> syntaktische Seite) an ein paar Nachmittagen realisieren lassen.
Bei einer einfach gehaltenen Programmiersprache ist das kein Thema. Mit
ein wenig Übung geht das flott.
Ich nehm gern den COCO/R. Hauptsächlich deshalb, weil das Teil an meiner
Alma Mater entwickelt wurde und ich gut damit klar komme, dass ein
rekursiver Abstieg erzeugt wird.
Dazu dann noch eine Variablenverwaltung und eine Stackmaschine.
Ich mach mir die Sache meistens einfach: So wie bei Ur-Pascal muss alles
in einem File sein. Damit brauch ich keinen ausgefuchsten Linker und
Funktionsaufrufe sind einfach umzusetzen (sofern ich Funktionsaufrufe
überhaupt vorsehe). Auch specke ich oft beim Scoping von Variablen etwas
ab (alles ist global :-). Kommt halt immer drauf an, was der geplante
Einsatzzweck der Sprache ist und was ich so an typischen Programmgrößen
erwarte.
>So wie bei Ur-Pascal muss alles in einem File sein.
Das wäre/ist auch mein bevorzugter Ansatz für einen ersten Interpreter.
Vor allem lässt sich dann (je nach Umfang der Sprache) das ganze "mal
eben" auf einen AVR portieren.
Interessant wäre auch (vllt später als Erweiterung) das ganze von einer
SD-Karte online Interpretieren zu lassen. Dann könnte man noch u.u.
zusätzliche Module (also mehrere Programm-Dateien) laufen lassen. Mal
schauen was mir da noch für Schweinereien einfallen :-))
Aber COCO/R sagt mir nichts. Müsst ich mir mal anschauen. Hört sich
interessant an.
Hallo,
hab nun weitere Sachen geändert, waren einige Fehler drin, habs erst
später gemerkt, das ich in der syntaxtree.h file noch die LabelK und
JmpK eintragen muss, aber leider kommen immer noch zwei Feher:
JMP Undeclared (first use in function)
NAME undeclared (...........)
die sind soch bereits definiert warum kommt dann diese Fehlermeldung.
Danke für deine Hilfe.
Ich häng mal die neuen dateien.
Gruß
Ali
Hallo,
hab den Fehler gefunden jtzt funktioniert das programm, hatte vergessen
das .tab.h file umzubenennen.
danke für deine Hilfe und deine Geduld, bei Fragen melde ich mich
wieder.
Einen schönen Tag noch.
MfG
Ali
> jtzt funktioniert das programm
Das glaub ich nicht
1
label_stmt:':'NAME
2
{$$=newStmtNode(LabelK);
3
$$->child[0]=$2;
4
5
}
6
7
jmp_stmt:JMPNAME
8
{$$=newStmtNode(JmpK);
9
$$->attr.name=strdup($2);
Fällt dir was auf?
Das eine mal behandelst du einen Namen, indem du ihn als child in den
Baum einhängst, das andere mal, in dem du dir einen Pointer auf den
String in attr.name merkst.
Ja, wie ist das nun bei Namen? Sind das tatsächlich Child Knoten?
Dazu kommt noch
1
caseLabelK:
2
printf("Label: %s\n",tree->attr.name);
3
break;
4
caseJmpK:
5
printf("JMP\n");
6
break;
Aha. Bei einem Label, hast du zwar den Labelnamen im Child Knoten
gespeichert (hat denn dein Compiler gar nichts dazu gesagt?), die
Ausgabe machst du aber vom attr.name Feld.
Beim Jump wird (sicherheitshalber) erst mal gar nichts ausgegeben.
Ich würde sagen: von funktionieren bist du noch weit entfernt.
Dein Programm geht vielleicht durch den Compiler (wobei es allerdings
Warnungen geben müsste. Auch in C kann man nicht straflos einen char
Pointer an einen TreeNode Pointer zuweisen), aber funktionieren tut es
nicht.
Hallo,
hab das programm ausgeführt, läuft auch wie gehabt, leider funktioniert
das nicht ganz mit dem JMP befehl. Kannst du mir einen weiteren rat
geben was noch fehlt, oder mache ich da einen Gedankenfehler.
Gruß
Ali
Hallo,
also der Compiler hat ga rnichts gesagt und es gab auch keine Warnung,
na gut dachte jetzt sollte es funktionieren hat es aber leider nicht
getan.
Und das Chaos mit der Speicherung von Strings hast du auch noch nicht
nahcgeschlagen, bzw. bereinigt
Aus der FAQ
1
11.3 Strings are Destroyed
2
My parser seems to destroy old strings, or maybe it loses track of
3
them. Instead of reporting ‘"foo", "bar"’, it reports
4
‘"bar", "bar"’, or even ‘"foo\nbar", "bar"’.
5
6
This error is probably the single most frequent “bug report” sent to Bison lists, but is only concerned with a misunderstanding of the role of the scanner. Consider the following Lex code:
7
8
%{
9
#include <stdio.h>
10
char *yylval = NULL;
11
%}
12
%%
13
.* yylval = yytext; return 1;
14
\n /* IGNORE */
15
%%
16
int
17
main ()
18
{
19
/* Similar to using $1, $2 in a Bison action. */
20
char *fst = (yylex (), yylval);
21
char *snd = (yylex (), yylval);
22
printf ("\"%s\", \"%s\"\n", fst, snd);
23
return 0;
24
}
25
26
If you compile and run this code, you get:
27
28
$ flex -osplit-lines.c split-lines.l
29
$ gcc -osplit-lines split-lines.c -ll
30
$ printf 'one\ntwo\n' | ./split-lines
31
"one
32
two", "two"
33
34
this is because yytext is a buffer provided for reading in the action, but if you want to keep it, you have to duplicate it (e.g., using strdup).
Aha, Hatte ich doch wieder mal den richtigen Riecher.
Strings müssen mittels strdup dupliziert werden. Du bekommst immer nur
Pointer auf Strings, die temporär sind.
Ali imran Zafar schrieb:> Hallo,> hab das programm ausgeführt, läuft auch wie gehabt,
Das y-File, das du gepostet hast, KANN nicht funktionieren
1
label_stmt:':'NAME
2
{$$=newStmtNode(LabelK);
3
$$->child[0]=$2;
Hier speicherst du einen Pointer auf den Namen in child[0].
Für die Ausgabe ...
1
caseLabelK:
2
printf("Label: %s\n",tree->attr.name);
... benutzt du aber den attr.name Pointer.
Das KANN nicht funktionieren. Da brauch ich das noch nicht einmal
compilieren und testen um das zu sehen.
Wenn man ein Zwischenergebnis einer Variablen zuweist, dieses
Zwischenergebnis dann von einer ganz anderen Variablen auszulesen
versucht, dann KANN da niemals der richtige Wert rauskommen.
(Und Hinweis: attr.name wäre richtig gewesen)
Ali imran Zafar schrieb:> Kannst du mir einen weiteren rat> geben was noch fehlt, oder mache ich da einen Gedankenfehler.
Das Problem ist, das es bei deinem Kentnissstand mit einem Rat oder Tip
nicht getan ist.
Ich denke bereits jetzt mit Schaudern an die Funktion (die du noch nicht
hast), die im Baum nach einem Label sucht um dann letztendlich die Jumps
auflösen zu können. Das wird eine rekursive Funktion werden und ob ich
dir die in verdauliche Brocken aufdröseln kann, weiß ich ehrlich gesagt
noch nicht.
Hallo,
wollt mal fragen, label ist doh ein Nichtterminalsymbol, folglich
brauche ich doch kein extra Token NAME, label besteht doch aus einem
doppelpunkt und Bezeichner, also ':' ID sollte doch reichen.Danke.
Gruß
Ali
So nach langem suchen und viel lesen bin ich zu diesem schluss gekommen:
in lex:
":" {return yytext[0];}
in yacc:
label_stmt: ':' ID
{ $$ = newStmtNode(LabelK);
$$.attr.name = $2;
}
jmp_stmt: JMP ID
{$$ = newStmtNode(JmpK);
$$.attr.name = $2;
}
ist das so ungefähr in ordnung,
aber leider kommt eine warnung:
nonterminal useless in grammar: label_stmt
1 rule useless in grammar: label_stmt: ':' ID
was hat das zu bedeuten.danke.
Gruß
Ali
Guten Morgen,
ich muss da was korrigieren da ist noch ein kleiner Fehler:
in lex:
":" {return yytext[0];}
in yacc:
label_stmt: ':' ID
{ $$ = newStmtNode(LabelK);
$$->attr.name = $2;
}
jmp_stmt: JMP ID
{$$ = newStmtNode(JmpK);
$$->attr.name = $2;
}
so kompiliert er ohne Fehler.Danke.
Gruß
Ali
Hallo,
ich habe jetzt alles hinbekommen und hab auch weitere sachen eingefügt
wie operatoren: >=, <=, != und logische Operatoren and, or.
Nun bin ich dabei die Inkrement und dekrement Operatroen zu deklarieren,
wenn ich in der Eingabe ++ eingebe dann klappt auch alles wuderbar gibt
auch aus INC wurde benutzt, nun will ich, das wenn "++x" eingegeben
wird, dass dann ausgegeben wird:
INC
Id:x
aber der compiler erkennt x nicht, ich habe so definiert, wie bei dem
JMP Befehl, aber leider geht das nicht, habs auch probiert mit
"++"{return yytext[0];} klappt leider auch nicht, wäre nett wenn du mir
da helfen könntest, hab die aktuellste files in
http://bitbucket.org/matrix/changed-tiny aktualisiert.Danke.
Gruß
Ali
hi,
I m newbie to flex-bison. I found this post very helpful with respect to
learning. I am using compiler.y and compiler.l from the below links.
flex-bison.http://bitbucket.org/matrix/changed-tiny
If i use this input to parse
a = 5;
b = 6;
print(a);
scan(b);
compiler.y works fine and output the parse tree.
I am facing the problem to parse the if condition in the above
compiler.y
when But i run this input
a = 5;
b = 6;
if(a <=5 )
a = b;
end
it gives the syntax error on line 3 for if statement. Compiler.y is not
parsing the if condition.I am unable to figure out if there is some
thing wrong with input or grammar compiler.y. Same is the case for
while/for statements.
Please share sample input file to parse the whole grammar so that i can
debug and run compiler or there is something incorrect with grammar.
Please help. Thanks in advance