Parsen mit java_cup - Shift/Reduce Problem

adalbert

Mitglied
Hallo zusammen!

Ich bastle gerade an einer Templateing-eingine und definiere definiere dafür eine eigene, kleine Sprache. Die Sprache soll mit JFlex und java_cup analysiert werden und damit ein Code-Baum erzeugt werden, welcher dann interpretiert wird.

Ich hab dabei u.a. folgende Anforderung: Es gibt if-statements, welche conditions auswerten und je nach dem ihren "Körper" ausführen oder nicht.
Eine Art der Conditions sind Relationale Bedingungen, also Vergleiche von Werten, wie 5 >= 3, a < 7 oder b + 4 < 6 *(a -1). Neben numerischen Werten gibts auch Strings und logische Werte, welche bei Vergleichen auch berücksichtigt werden müssen (also z.B. a == "hallo welt" soll möglich sein)!
a und b sind hier Variablen, die über ihren Namen (= Identifier) angesprochen werden können.

Der Kern meines Problem sieht so aus (ohne actions o. überflüssiges Zeug):

Code:
// ########################################################
// # Terminals
// ########################################################

terminal        LPA, RPA;                       // ( and )
terminal        STRING, BOOLEAN, INT, WORD;     // Literals for Strings, Logic values and numeric values
terminal        EQ, LT;                         // rel. operators == <
terminal        IF, THEN, ENDIF;
terminal        BODY;                           // all real statements
terminal        PLUS, MINUS;                    // arith. operators
terminal        IDENTIFIER;                     // identifier which can be resolved to a value

// ########################################################
// # NonTerminals
// ########################################################

non terminal    prog;                           // the goal
non terminal    if;
non terminal    condition;

non terminal    num_lit, str_lit, log_lit;      // literals for numeric, string and logic values
non terminal    id;
non terminal    value;                          // any kind of value
non terminal    arith;                          // any kind of arith. expression

// ########################################################
// # Precedences
// ########################################################

precedence left PLUS, MINUS;
precedence left LPA;

// ########################################################
// # Production rules
// ########################################################

prog    ::= if;

if      ::= IF condition THEN BODY ENDIF;

condition
        ::= value EQ value
        |   value LT value
        ;
        
value   ::= str_lit
        |   log_lit
        |   arith
        |   id
        ;
        
str_lit ::= STRING;

log_lit ::= BOOLEAN;

num_lit ::= INT
        |   WORD
        ;
        
arith   ::= arith PLUS arith
        |   arith MINUS arith
        |   LPA arith RPA
        ;

arith   ::= num_lit
        |   id
        ;
        
id      ::= IDENTIFIER
        ;

Wie man sieht ist das nonterminal id sowohl in der Regel für value als auch für arith enthalten. Das das nicht gut geht und es zu Konflikten kommt, ist mir mittlerweile klar, nur weis ich nicht, wie ich das lösen soll.

Die Vergleiche an sich sind so implementiert, dass sie nur einen Wert (value) wollen und zur Laufzeit entscheiden ob die beiden Werte überhaupt verglichen werden können. Ich kann auch zur Laufzeit bestimmen ob hinter a ein String oder ein DWORD oder sowas steht (Typ ist also bekannt).

Weis jemand wie man so ein Problem lösen kann? Mir raucht schon ganz schön der Kopf...

Gruß,
adalbert
 
inwiefern besteht denn überhaupt ein Problem, hast du konkrete Fehlermeldungen von einem Framework welches mit diesen Regeln gefüttert wird oder so?

was mir auffällt:
"arith ::= " steht zweimal da, arith wird als einziges Element 2x definiert, ist das ok?
 
Hallo SlaterB,
die Meldung die CUP ausgibt lautet
Code:
Warning : *** Reduce/Reduce conflict found in state #5
  between arith ::= id (*)
  and     value ::= id (*)
  under symbols: {EQ, LT, THEN}
  Resolved in favor of the second production.

Warning : *** Shift/Reduce conflict found in state #5
  between arith ::= id (*)
  under symbol EQ
  Resolved in favor of shifting.

Warning : *** Shift/Reduce conflict found in state #5
  between arith ::= id (*)
  under symbol LT
  Resolved in favor of shifting.

Warning : *** Shift/Reduce conflict found in state #5
  between arith ::= id (*)
  under symbol THEN
  Resolved in favor of shifting.

Warning : *** Shift/Reduce conflict found in state #5
  between value ::= id (*)
  under symbol EQ
  Resolved in favor of shifting.

Warning : *** Shift/Reduce conflict found in state #5
  between value ::= id (*)
  under symbol LT
  Resolved in favor of shifting.

Warning : *** Shift/Reduce conflict found in state #5
  between value ::= id (*)
  under symbol THEN
  Resolved in favor of shifting.

Daran sieht man eben (in der ersten Meldung), dass er nicht weis, ob er aus id einen value oder ein arith machen soll (ist eben nicht eindeutig).

Das arith ::= doppelt vorkommt ist kein Problem. Das darf man machen um z.B. die Lesbarkeit zu erhöhen indem man logisch zusammengehörige Regeln zusammenfasst.
 
nicht dass ich wirklich was verstehe, hatte auf meinem Doppler gehofft, jetzt mit Erzwingen der Fehlermeldung sicher was beigetragen 😉
und sonst zumindest ne Stunde auf andere Antworten abgewartet,

aber einen Versuch gönne ich mir noch:
kann eine arith direkt zu einer id ausgewertet werden?
dann lasse doch bei value nur arith zu, darüber kann es letztlich auch id werden

generell sollte aber sicher besser funktionieren..
 
Wie slaterb sagt einfach das id bei value rausnehmen. Ansonsten muss ich sagen dass das alles etwas wirr aussieht.
Wenn du auch Dinge wie "if a then...." oder "if (a < b) == (d < e) then ..." zulassen willst, dann geht das ganze imo auch einfacher:

Code:
condition ::= simple
    | condition + condition
    | condition - condition
    | condition < condition
    | condition > condition
    | LPA condition RPA

simple ::= STRING | BOOLEAN | ...
Die Bindungskraft der Operatoren kannst du dann schön der precedence festlegen.
 
Hallo SlaterB,

es ist leider nicht möglich eine arith (== Arithmetic expression) in eine id "umzuwandeln". Hinter einer id steht eben zur Laufzeit eine Variable und deren Inhalt möchte ich, sofern dieser ein numerischer Wert ist, in einer arith benutzen.

Da das auch für String-Werte und logische Werte gelten soll sehe ich kaum eine andere Möglichkeit als zu sagen, dass ein value durch eine id gegeben sein kann oder aber durch ein Literal.

Und da habe ich eben diese Mehrdeutigkeit (value -> arith -> id bzw. value -> id).

Ich hab auch schon überlegt, die Regel für arith wie folgt zu definieren:

Code:
arith   ::= arith PLUS arith
        |   arith MINUS arith
        |   LPA arith RPA
        ;

arith   ::= num_lit
        |   value
        ;

Also value statt id, was aber zu einer Rekursion führt und mich auch nicht weiter bringt....

Natürlich hast du recht, die Meldung vom CUP hätte ich schon mit beifügen sollen 🙂
 
Hallo HoaX,

ich weis nicht so recht, ob es gut ist zu sagen, dass man Bedingungen "addieren" oder miteinander vergleichen kann (condition + condition bzw. condition < condition).

Ich denke mir einfach, dass eine Berechnung nichts anderes darstellt als einen Wert. Und ein Wert kann sein:
* Ein Literal (Also Strings, Boolean und Zahlen), also etwas was direkt in den Quellcode geschrieben wird.
* Das Ergebnis einer Berechnung
* Der Wert einer Variablen

Zahlen werden oben nicht direkt als Literal erkannt, sondern in eine "Berechnung" gepackt und deren Wert wird eben dann genutzt.

Wie könnte man das einfacher lösen?
 
Ich sehe trotz deiner Erklärung keinen Grund wieso du nicht id bei value entfernen kannst.

Ich denke es wäre einfacher wenn du mal ein paar aussagekräftige Beispiele beifügst was gehen soll, und ebenso was nicht gehen soll.
 
> es ist leider nicht möglich eine arith (== Arithmetic expression) in eine id "umzuwandeln"

verstehe ich nicht, steht das nicht im direkten Widerspruch zu deiner Regel
> arith ::= id ;
?

nicht umwandeln, sondern auswerten, es kann eine id als eine arith gelten?
dann auch ein value als id, denn ein value kann ein arith sein?

----

kann ein value letzlich ein num_lit sein? wieso ist das über den Umweg arith eingebaut,
aber num_lit bei value selber nicht aufgezählt so wie id bei beiden?
 
Hier ein paar Beispiele:

if ("hallo welt" == a) then ...
if (TRUE == b) then
if (5+4 >= c) then
if (3*c + d != a) then
if (a * 4 > "Hallo Welt") then -> macht keinen sinn, soll aber nicht fehlschlagen; semantische Analyse wird das dann finden!

Wenn ich die id aus value rausnehme, dann geht z.B. if ("hallo welt" == a) then nicht mehr, weil der parser versucht aus a ein arith zu machen. Das arith kann aber nur mit numerischen Werten gebildet werden.

Ich könnte aber versuchen den Wert trozdem im Arith zu speichern.
Schonmal Danke für eure Vorschläge!

-----------
@ SlaterB:
Es ist möglich einen numerischen Wert aus einer id zu laden und zu benutzen (z.B. in einer Berechnung)
Es ist aber nicht möglich den Wert einer Berechnung in eine ID zu schreiben (zumindest solange im Quellcode nichts anderes steht)
 
Zuletzt bearbeitet:
Hallo HoaX,

ich weis nicht so recht, ob es gut ist zu sagen, dass man Bedingungen "addieren" oder miteinander vergleichen kann (condition + condition bzw. condition < condition).

Ich denke mir einfach, dass eine Berechnung nichts anderes darstellt als einen Wert. Und ein Wert kann sein:
* Ein Literal (Also Strings, Boolean und Zahlen), also etwas was direkt in den Quellcode geschrieben wird.
* Das Ergebnis einer Berechnung
* Der Wert einer Variablen

Zahlen werden oben nicht direkt als Literal erkannt, sondern in eine "Berechnung" gepackt und deren Wert wird eben dann genutzt.

Wie könnte man das einfacher lösen?

Darum meine Frage was es sonst noch alles können soll. Da wären mehr gültige und auch ungültige Beispiele hilfreich.

Wenn du keine Addition von Condition magst, dann zieh die eben raus, wie du es schon hast, dann kommst du zu sowas ungefähr:
Code:
condition ::= expression LT expression
    | expression EQ expression

expression ::= expression + expression
    | expression - expression
    | LPA expression RPA
    | simple

simple ::= NUMBER | STRING | ...
 
Hallo ihr beiden!

eben hat es einen lauten Knall gemacht und der Denk-Knoten in meinem Kopf ist geplatzt. Ihr habt vollkommen recht, ich hab da zu kompliziert gedacht. Manchmal hat man einfach ein Brett vorm Kopf.

Wenn ich schon sage, dass alles ein Wert ist, kann ich auch sagen, dass ich mit Werten rechne und muss nicht versuchen von den Werten nur die numerischen Werte für die Berechnungen rauszufischen.

FYI ich habs jetzt so gemacht (Das entspricht so ziemlich dem was HoaX gepostet hat):

Code:
value   ::= STRING
        |   BOOLEAN
        |   INT
        |   WORD
        |   id
        |   LPA value RPA
        ;

value   ::= value PLUS value
        |   value MINUS value
        ;

condition  ::= value EQ value
        |   value LT value
        ;

Ob das was sinnvolles gibt, kann man sehr leicht dann später analysieren sobald der Baum fertig ist.

You 2 made my day!
Danke nochmals!
 
Spaßig wird es wenn du Ausdrücke wie "if -6 < 5 then..." zulassen willst, aber nicht "if 6 --5 < 4 then..." 😉 Ich hab da bestimmt einen Tag dran gesessen bis es Klick gemacht hatte und die völlig offensichtliche Lösung hatte.
 

Neue Themen


Zurück
Oben