ANTLR .. parse tree erzeugen

diggaa1984

Top Contributor
Hiho an alle,

ich habe gerade ein mittelschweres Problem bei der Konzeptfindung :)

Zur Struktur:
Ich nutze ANTLR um mir zur Laufzeit meines Programms dynamisch Parser und Lexer für eine Grammatik zu erzeugen. Diese Grammatik kann zur Laufzeit vom Nutzer importiert werden (in EBNF - Notation). Ich nutze eine template-Datei, in welche ich die EBNF-Regeln des Nutzers hineinkopiere:
Code:
grammar GRAMMARSYNONYM;

options {
	backtrack=true;
	memoize=true;
  	output=AST;
}

/*------------------------------------------------------------------
 * parser and lexer rules
 *------------------------------------------------------------------*/

RULES


IDENT : LETTER (DIGIT | LETTER)* ;
fragment LETTER : ('a'..'z'|'A'..'Z'|'_') ;

INT : (DIGIT)+ ;
FLOAT : INT+ '.' INT+ ;
fragment DIGIT: ('0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9') ;

WHITESPACE : ( '\t' | ' ' | '\r' | '\n'| '\u000C' )+ 	{ $channel = HIDDEN; } ;

Die Regelmenge wird an der Stelle von RULES einfach hineinkopiert. Wenn seitens ANTLR es Fehler beim Erstellen der Lexer und Parser gibt, wird die Fehlermeldung einfach an den Nutzer weitergeleitet. Sollte ANTLR erfolgreich Parser und Lexer erstellt haben, so erzeuge ich nun wie folgt die Klassen und kann sie auch erstmal erfolgreich testen:
Java:
// Create a File object on the root of the directory containing the class files
File file = new File("Grammars/" + grammar +"/bin/");
try {
    // Create a new class loader with the directory
    ClassLoader cl = new URLClassLoader(new URL[]{file.toURI().toURL()});

    // Load in the classes
    ANTLRFileStream fStream = null;                
    Lexer lexer = (Lexer) (cl.loadClass(grammar + "Lexer")).newInstance();
    CommonTokenStream tokens = new CommonTokenStream(lexer);
                
    Class<?> parserClass = cl.loadClass(grammar + "Parser");
    Constructor<?> parserCTor = parserClass.getConstructor(TokenStream.class);
    Parser parser = (Parser) parserCTor.newInstance(tokens);
    Method entryPointMethod = parserClass.getMethod("start");
                
    File testDir = new File("Grammars/Tests/" + grammar + "/");
    File[] testFiles = testDir.listFiles();
    if (testFiles == null)
        return;
                
    //test sample input files and reuse of lexer/parser objects
    for (File testFile: testFiles) {
        parser.reset();
        lexer.reset();
        tokens.setTokenSource(lexer);
                	
        if (fStream == null) {
            fStream = new ANTLRFileStream(testFile.getAbsolutePath());
            lexer.setCharStream(fStream);
        } else {
            fStream.load(testFile.getAbsolutePath(), null);
        }//if
                	
        System.out.println("parsing: " + testFile.getName());
        System.out.println("Tokens: " + tokens.getNumberOfOnChannelTokens());
        long start = System.nanoTime();
        entryPointMethod.invoke(parser);
        long end = System.nanoTime() - start;
        System.out.format("Duration: %1.4f s\n",((end)/1000000000.0));
        System.out.println("Errors: " + parser.getNumberOfSyntaxErrors() + "\n");
    }//for

Das klappt soweit super, nun brauche ich aber noch den ParseTree .. ein AST genügt mir nicht.

Ich habe nun diverse Seiten gesehen:
Einige erzeugen den Baum mittels umschreiben durch eine Tree-Grammatik, das kann ich nicht machen, da ich durch das dynamisch Einlesen der Grammatiken zur Laufzeit nichts über die Regelmengen weiss, und das Template diesbezüglich nicht um Tree-Operatoren erweitern kann.

Ein weiterer Ansatz besagt, dass ich das erzeugen der Lexer und Parser im debug-Modus durchführen soll. Nur da fliegt mir jedesmal ne Exception um die Ohren, da sofort beim Erstellen des Parsers
Java:
DebugParser parser = (DebugParser) parserCTor.newInstance(tokens);
versucht wird das erste Token zu lesen, was zu dem Zeitpunkt ja noch gar nich anliegt. Zumal mir der DebugModus da ein wenig zu viel erscheint.

Und durch das allgemeine Parser-Interface was ich nutze, komm ich auch an keine tree-Objects innerhalb der generierte Parser-Klasse ran, da diese ja nicht Bestandteil der übergeordneten Klassen sind. Oder gibt es eine Möglichkeit in Java aus einem dynamischen Klassennamen auch ein Object dieser Klasse zu casten? Ich habe quasi nur den Namen der Grammatik (zB. Foo) und erhalte ja von ANTLR dann: FooLexer.java und FooParser.java und nach dem compilieren dieser Datein entsprechende FooLexer.class und FooParser.class. Diese müsste ich quasi direkt instanziieren und Casten können, habe aber zur Compilezeit nur den String des Klassennamens zur Verfügung :/

Java:
FooParser parser = (FooParser) parserCTor.newInstance(tokens);

Da scheints mir dynamisch nix in ANTLR zu geben, womit ich nun weiterkomme. Ich benötige den Parse Tree aber unbedingt, da weitere Funktionen des Programms darauf aufbauen. ???:L
 

Neue Themen


Oben