2014-04-16 13 views
5

Bisher habe ich das ganze "The Definitive ANTLR 4 Reference" Buch sowie viele Websites für eine Antwort auf meine Frage gesucht und ich kann immer noch nichts finden auf der Thema.Besucher/Listener-Code für eine While-Schleife in ANTLR 4

Ich benutze ANTLR 4, um eine Untergruppe der C-Sprache zu erstellen, die einige grundlegende Funktionen ausführt. Worüber ich keine Ahnung habe, ist die Implementierung einer einfachen while-Schleife in meiner Visitor-Klasse. Bisher habe ich das in meiner Grammatik:

grammar Expr; 

prog: stat+ ; 

stat: expr NEWLINE    # printExpr 
    | ID '=' expr NEWLINE   # assign 
    | loop NEWLINE    # whileLoop 
    | relational NEWLINE   # relat 
    | NEWLINE      # blank 
    ; 

expr: expr op=('*'|'/') expr  # MulDiv 
    | expr op=('+'|'-') expr  # AddSub 
    | INT       # int 
    | ID       # id 
    | '(' expr ')'    # parens 
    ; 

relational:  expr op=(GREATER|LESS) expr  # GreaterEqual 
      ; 

loop: 'while' '('relational')' NEWLINE? '{'stat*'}' #while 
    ; 

GREATER : '>' ; 
LESS : '<' ; 
MUL : '*' ; // assigns token name to '*' used above in grammar 
DIV : '/' ; 
ADD : '+' ; 
SUB : '-' ; 
ID : [a-zA-Z]+ ;  // match identifiers 
INT : [0-9]+ ;   // match integers 
NEWLINE:'\r'? '\n' ;  // return newlines to parser (is end-statement signal) 
WS : [ \t]+ -> skip ; // toss out whitespace 

So könnte ich mehrere Anweisungen innerhalb einer While-Schleife haben. Meine Besucher Klasse sieht wie folgt aus:

public class EvalVisitor extends ExprBaseVisitor<Integer> { 

/** "memory" for our calculator; variable/value pairs go here */ 
Map<String, Integer> memory; 

public EvalVisitor() { 
    memory = new HashMap<String, Integer>(); 
} 

/** ID '=' expr NEWLINE */ 
@Override 
public Integer visitAssign(ExprParser.AssignContext ctx) {  
    String id = ctx.ID().getText(); // id is left-hand side of '=' 
    int value = super.visit(ctx.expr()); // compute value of expression on right   
    memory.put(id, value);   // store it in our memory 
    return value; 
} 

/** expr NEWLINE */ 
@Override 
public Integer visitPrintExpr(ExprParser.PrintExprContext ctx) { 
    Integer value = super.visit(ctx.expr()); // evaluate the expr child 
    System.out.println(value);   // print the result 
    return 0;       // return dummy value 
} 

/** INT */ 
@Override 
public Integer visitInt(ExprParser.IntContext ctx) { 
    return Integer.valueOf(ctx.INT().getText()); 
} 

/** ID */ 
@Override 
public Integer visitId(ExprParser.IdContext ctx) { 
    String id = ctx.ID().getText(); 
    if (memory.containsKey(id)) return memory.get(id);   
    return 0; 
} 

/** expr op=('*'|'/') expr */ 
@Override 
public Integer visitMulDiv(ExprParser.MulDivContext ctx) { 
    int left = super.visit(ctx.expr(0)); // get value of left subexpression 
    int right = super.visit(ctx.expr(1)); // get value of right subexpression 
    if (ctx.op.getType() == ExprParser.MUL) return left * right; 
    return left/right; // must be DIV 
} 

/** expr op=('+'|'-') expr */ 
@Override 
public Integer visitAddSub(ExprParser.AddSubContext ctx) { 
    int left = super.visit(ctx.expr(0)); // get value of left subexpression 
    int right = super.visit(ctx.expr(1)); // get value of right subexpression   
    if (ctx.op.getType() == ExprParser.ADD) return left + right; 
    return left - right; // must be SUB 

} 

/** '(' expr ')' */ 
@Override 
public Integer visitParens(ExprParser.ParensContext ctx) { 
    return super.visit(ctx.expr()); // return child expr's value 
} 

@Override 
public boolean visitGreaterEqual(GreaterEqualContext ctx) { 
    int left = super.visit(ctx.expr(0)); 
    int right = super.visit(ctx.expr(1)); 

    if(ctx.op.getType() == ExprParser.GREATER) { 
     return left > right; 
    } 
    else { 
     return left < right; 
    } 

} 

@Override 
public Integer visitWhileLoop(WhileLoopContext ctx) { 

    if(visit(ctx.getRuleContext())) { 

    } 

    return super.visitWhileLoop(ctx); 
} 

}

Der meiste Code in der Besucher Klasse i aus dem Buch nahm, weil ich gerade bin angefangen mit ANTLR 4. Ich finde es schwer zu glauben, dass, abgesehen Von der Grammatik für die While-Schleife wird nicht erwähnt, wie man Besucher/Listener oder Aktionen für eine einfache While-Schleife in dem von Terence Parr geschriebenen Buch implementiert. Kann mir jemand helfen, einen Besucher-/Listener-Java-Code für eine While-Schleife zu schreiben?

+0

Hallo, das ist mehr Inhalt von "Language Implementation Patterns": http: //www.amazon/Language-Implementation-Patterns-Domain-Specific-Programmierung/dp/193435645X/ref = pd_bxgy_b_img_y –

+0

@TheANTLRGuy Ich habe dieses Buch und schaute es mir kurz an, um zu sehen, ob ich finden konnte, wie man eine While-Schleife implementiert und Leider tut es das nicht. – Greg

+0

Haben Sie sich das Muster namens Baum-basierter Interpreter angesehen? Seite 230. Es macht nicht speziell while-Schleifen, die ich denke, aber es bringt Colin zurück, die viel komplizierter ist. Es zeigt deutlich, wie man einen virtuellen Programmzähler durch einen Baum bewegt. –

Antwort

10

Ich finde es schwer zu glauben, dass, abgesehen von der Grammatik für die while-Schleife, nicht erwähnt wird, wie man Besucher/Listener oder Aktionen für eine einfache While-Schleife in dem von Terence Parr geschriebenen Buch implementiert.

Das liegt daran, dass die ANTLR Referenz über ANTLR ist, die über das Parsen ist, nicht über die Bühne nach Parsing.

Sie können dies nicht tun:

@Override 
public boolean visitGreaterEqual(GreaterEqualContext ctx) { 
    ... 

Du hast bereits Ihre Besucher erklärt Integer s zurück, so dass das, was jede Regel zurückgeben sollte. Entweder machen einen benutzerdefinierten Wrapper Value, die Ihre Sprache Werte kapselt (Zahlen, string, booleans), oder einfach nur 0 zurück, wenn eine Beziehung Ausdruck falsch:

@Override 
public Integer visitGreaterEqual(ExprParser.GreaterEqualContext ctx) { 
    int left = this.visit(ctx.expr(0)); 
    int right = this.visit(ctx.expr(1)); 

    if (ctx.op.getType() == ExprParser.GREATER) { 
     return left > right ? 1 : 0; // 0 is false (all other values are true) 
    } 
    else { 
     return left < right ? 1 : 0; 
    } 
} 

Dann können Sie Ihre while schreiben wie folgt:

@Override 
public Integer visitWhile(ExprParser.WhileContext ctx) { 

    // Evaluate the relational expression and continue the while 
    // loop as long as it is true (does not equal zero). 
    while (this.visit(ctx.relational()) != 0) { 

     // Evaluate all statements inside the while loop. 
     for (ExprParser.StatContext stat : ctx.stat()) { 
      this.visit(stat); 
     } 
    } 

    // 0 now also is false, so maybe return null instead which would be 
    // some sort of VOID value (or make a proper Value class). 
    return 0; 
} 

Beachten Sie, dass der obige Code fehlschlägt, wenn while Anweisungen verschachtelt werden, da die innere while 0 zurückgeben würde, die äußere Schleife zu stoppen. In solchen Fällen wäre es besser, eine benutzerdefinierte Value Klasse zu erstellen und eine Art Value.VOID Instanz einzuführen, bei der die Schleife nicht gestoppt wird.

Ausführen des folgenden Hauptmethode:

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

    String expression = "n = 1\n" + 
      "while (n < 10) {\n" + 
      " n\n" + 
      " n = n + 1\n" + 
      "}\n"; 
    ExprLexer lexer = new ExprLexer(new ANTLRInputStream(expression)); 
    ExprParser parser = new ExprParser(new CommonTokenStream(lexer)); 
    new EvalVisitor().visit(parser.prog()); 
} 

würde drucken:

1 
2 
3 
4 
5 
6 
7 
8 
9

haben auch einen Blick auf diese Demo Sprache, die ANTLR4 und if und while Aussagen sowie eine benutzerdefinierte verwendet Value Objekt: https://github.com/bkiers/Mu

+0

Vielen Dank Bart. Ihre Lösung wirkt Wunder! Eine Frage jedoch - ich versuche, meinen Kopf darüber zu halten, was in den Besuchermethoden gemacht wird, die all das funktionieren lassen. Nehmen Sie zum Beispiel Ihre Implementierung der if-Anweisung: Was Sie tun, sammelt alle Bedingungen + ihre Code-Blöcke in einer Liste. Dann prüfen Sie, welche der Bedingungen zutrifft, so dass Sie dann den Anweisungsblock aufrufen können, der dieser bestimmten Bedingung entspricht. Aus diesem Grund haben Sie Ihren Bedingung + -Anweisungsblock zusammengeführt, um den Block, der ausgeführt werden soll, leicht zu finden. Hab ich recht? – Greg

+0

@Greg, ja, das ist richtig. –

+0

Danke nochmal Bart :) – Greg