triviale JavaCC Grammatik inkorrekt

Status
Nicht offen für weitere Antworten.

Professor Chaos

Aktives Mitglied
Hallo,

Ich habe den folgenden JavaCC-Code (der korrekt und zu fehlerfreiem Java-Code kompiliert wird):
Code:
PARSER_BEGIN(Test)

public class Test {

  public static void main(String args[]) throws ParseException {

    String tmp = "";
    for(int i=0 ; i<args.length ;i++)
      tmp+=args[i]+" ";

    java.io.StringReader sr = new java.io.StringReader( tmp.trim() );
    java.io.Reader r = new java.io.BufferedReader( sr );
    Test parser = new Test( r );
    parser.test();
  }

}

PARSER_END(Test)

TOKEN :
{
   < ONE: (["a"-"z","A"-"Z","/","\\","0"-"9","_"])+ >
|  < TWO: (["a"-"z","A"-"Z","/","\\","0"-"9","_"])+ >
}

void test() :
{ Token a=null,b=null;
}
{
  a=<ONE> " " b=<TWO>
    { if(a!=null) System.out.println("ONE: " + a.image);
      if(b!=null) System.out.println("TWO: " + b.image);
    }
}

Starte ich nun die Javaklasse Test.java mit den beiden Parametern 123 456, so wird NICHT 123 als ONE erkannt und entsprechend 456 als TWO, sondern ich erhalte den folgenden Parse-Error:

Code:
Exception in thread "main" parser.examples.ParseException: Encountered "456" at line 1, column 5.
Was expecting:
    <TWO> ...
    
	at parser.examples.Test.generateParseException(Test.java:193)
	at parser.examples.Test.jj_consume_token(Test.java:132)
	at parser.examples.Test.test(Test.java:22)
	at parser.examples.Test.main(Test.java:15)

Ich verstehe nicht, wieso... Das Blank als Trennzeichen sollte doch ausreichend sein.
Interessanterweise funktioniert das Parsen, falls ich das Plus hinter ONE weglasse und als ersten Parameter entsprechend nur "1" angebe. Wie schaffe ich das für einen beliebig langen String, wo liegt mein Fehler?
 

ps

Bekanntes Mitglied
javacc. wurx. das sieht ja scheusslich aus.
deine klammersetzung tut mir auch weh... aber ich schiebe das mal auf copy und paste ^^


Ich hab mir mal so ungefähr zusammengereimt was das machen soll. aber irgendwie ergibt das überhaupt keinen sinn für mich. mit reinem java wären das einige zeilen weniger. was ist ein Token. und was machst du mit den io readern für komisches zeug?

die zeile hier check ich garnicht:
a=<ONE> " " b=<TWO>

ist das sowas wie assert?
wieso schreibst du die nicht einfach untereinander.

bzw was soll das ganze programm eigentlich machen? :D
 

didjitalist

Bekanntes Mitglied
Das TOKEN Gehampel soll EBNF sein. Und die Grammatik lässt den gewünschten Ausdruck nicht zu. Dafür bräuchtest du einen entsprechenden dritten Token. Bzw. wahrscheinlich muss dein zweiter Token einfach eine Kombination von zweimal ONE sein, denn die sind derzeit ja identisch.
 

Professor Chaos

Aktives Mitglied
Hi, zunächst danke für die Antwort.

ps hat gesagt.:
javacc. wurx. das sieht ja scheusslich aus.
deine klammersetzung tut mir auch weh... aber ich schiebe das mal auf copy und paste ^^
Was sieht denn daran bitte scheußlich aus? Es ist ein Minimalbeispiel, was soll daran denn alles überflüssig oder unschön sein? Insbesondere verstehe ich nicht, von welchen falsch/unschön gesetzten Klammern du sprichst.

ps hat gesagt.:
Ich hab mir mal so ungefähr zusammengereimt was das machen soll. aber irgendwie ergibt das überhaupt keinen sinn für mich. mit reinem java wären das einige zeilen weniger. was ist ein Token.
Was heißt, es ergibt keinen Sinn? Es funktioniert nicht, ja, aber das sagte ich doch bereits!
Und ja, selbstverständlich wäre es mit reinem Java weniger Arbeit, aber der Sinn eines Minimalbeispiels ist doch nicht, großartige Projekte zu führen, sondern die Logik in einer Systematik zu begreifen.

ps hat gesagt.:
und was machst du mit den io readern für komisches zeug?
Komisches Zeug? Das ist die OFFIZIELLE (aus der offiziellen JavaCC FAQ) Vorgehensweise, um einen String zu parsen. In allen Tutorials macht man das mit System.in. Aber ich möchte nunmal einen String parsen, statt einer Eingabe, also ist das kein "komisches Zeug", sondern die Vorgehensweise, mit der man Strings verarbeitet.

ps hat gesagt.:
die zeile hier check ich garnicht:
a=<ONE> " " b=<TWO>

ist das sowas wie assert?
wieso schreibst du die nicht einfach untereinander.
bzw was soll das ganze programm eigentlich machen? :D
Nun gut, dann erkläre ich an dieser Stelle, was diese triviale Grammatik tun soll. Ich dachte allerdings, dass das offensichtlich sei.
Die Vorverarbeitung:
Meine Main Methode bekommt beliebig viele Parameter, die dank Java automatisch in agrs[0] bis args[args.length-1] gespeichert werden. Da ich aber nur einen String parsen kann, aber keinen String-Array, wandle ich diese Argumente in einen einzigen String um, die durch Leerzeichen getrennt werden. Starte ich das Programm z.B. mit den beiden Argumenten "123" und "456", so wird der Parser mit dem String "123 456" aufgerufen.
Das Parsen:
Ich schränke die Parameter, die eingegeben werden dürfen auf die obige Grammatik ein, es dürfen also nur sämtliche Buchstaben und Zahlen verwendet werden, sowie Unterstriche und Slashes. Die Zeile 'a=<ONE> " " b=<TWO>' soll als das Token a den ersten Parameter erkennen, der durch " " vom zweiten Parameter b getrennt wird. Und schließlich soll das ganze ausgegeben werden.
Und nochmal: Ja, selbstverständlich wäre das mit reinem Java einfacher, ich könnte schließlich einfach args[0] und args[1] abfragen, aber mir geht es doch darum, den Fehler in der Grammatik zu erkennen!

Ok, nun, da ich hoffe, dass mein Code etwas verständlicher wurde hoffe ich auf Antworten, die mich womöglich weiterbringen. Denn noch immer verstehe ich nicht, wieso "123" nicht erfolgreich als a und "456" als b erkannt wird.
 

Professor Chaos

Aktives Mitglied
didjitalist hat gesagt.:
Das TOKEN Gehampel soll EBNF sein. Und die Grammatik lässt den gewünschten Ausdruck nicht zu. Dafür bräuchtest du einen entsprechenden dritten Token. Bzw. wahrscheinlich muss dein zweiter Token einfach eine Kombination von zweimal ONE sein, denn die sind derzeit ja identisch.
Danke für die Antwort, diese bringt mich meinem Ziel schon etwas weiter. Leider nur etwas, denn ich verstehe sie nicht. Geht das vielleicht noch eine Spur ausführlicher?
Ich verstehe nicht, wieso mit 'a=<ONE> " " b=<TWO>' der String '123 456' nicht erfolgreich geparst wird. Eigentlich sollte doch von links nach rechts so lange versucht werden, das "<ONE>" aufzubauen, wie möglich (längster Präfix). Das wäre dann eindeutig "123", da das Leerzeichen in "<ONE>" nicht zulässig ist. Und danach sollte <TWO> aufgebaut werden.
Ich sehe nicht, was an meiner Grammatik falsch sein soll und wieso du glaubst, ich bräuchte ein weiteres Token. Auch verstehe ich überhaupt nicht, was du mit der Kombination der ONEs meinst...
 

Professor Chaos

Aktives Mitglied
Ich antworte nur, damit dieser Thread wieder eine etwas höhere Priorität bekommt. :)
Bisher wurde mir nämlich noch immer erfolgreich geholfen, nur dieses Mal nicht. Weiß denn keiner Rat?
 

Professor Chaos

Aktives Mitglied
Okay, nach (etwa) zwei Tagen weiterer intensiver Beschäftigung mit JavaCC konnte ich meine Frage selbst beantworten. Bevor ich dies aber tue, möchte ich JEDEM JavaCC-Anfänger das Tutorial (26 Seiten, 1A erklärt!) nahelegen, es lohnt sich allenfalls. Kapitel 1 eines Tutorials


Ok, hier zunächst der angepasste Code, danach folgt die Erklärung.

Code:
TOKEN : 
{ 
   < ONE: (["a"-"z","A"-"Z","/","\\","0"-"9","_"])+ > 
} 

void test() : 
{ Token a,b; 
} 
{ 
  a=<ONE> " " b=<ONE> <EOF>
    { System.out.println("ONE: " + a.image); 
      System.out.println("TWO: " + b.image); 
    } 
}

Okay, hier die Unterschiede:

Zunächst war es völlig sinnlos, <ONE> und <TWO> auf exakt dieselbe Art und Weise zu definieren. Denn JavaCC durchläuft die TOKEN-Definitionen von oben nach unten und akzeptiert diejenige, die als erstes erfolgreich erkannt wird. Damit konnte <TWO> NIEMALS gematcht werden, da immer zuerst <ONE> erkannt worden wäre.

Weiterhin fällt auf, dass ich das <EOF> nicht angehängt habe. Dies sollte man aber am Ende einer jeden Grammatik tun, da anderenfalls auch unerwünschte Dinge korrekt gematcht werden würden. Würde z.B. nach "123 456" nochmal etwas folgen, z.B. "123 456 789", so würde auch dies akzeptiert werden. Das <EOF> hingegen besagt, dass nach dem zweiten Parameter die Eingabe enden muss.

Nun noch ein Detail: In der neuen Version instanziiere ich die Tokens nicht mehr mit null. Auch teste ich vor den System.out-Ausgaben nicht mehr, ob die Tokens überhaupt gesetzt wurden. Das liegt daran, dass jeder Javacode, der sich direkt unter einer nicht-optionalen Grammatik befindet (dies ist hier der Fall) immer genau dann ausgeführt wird, falls die darüberliegende Grammatik tatsächlich matcht. Daher MÜSSEN a und b zugewiesen worden sein, da anderenfalls der Java-Code gar nicht erst ausgeführt worden wäre.

Ich hoffe, damit jemandem geholfen zu haben. :)
 
Status
Nicht offen für weitere Antworten.

Ähnliche Java Themen

Neue Themen


Oben