2014-04-16 10 views
5

Do tej pory przeszukałem całą książkę "The definitive ANTLR 4 Reference", jak również wiele stron, aby znaleźć odpowiedź na moje pytanie i nadal nie mogę znaleźć niczego na temat.Kod użytkownika/odbiornika pętli while w ANTLR 4

Używam ANTLR 4 do tworzenia podzbioru języka C, który wykonuje niektóre podstawowe funkcje. Nie mam pojęcia, jak zaimplementować prostą pętlę while w mojej klasie Visitor. Do tej pory mam to w mojej gramatyce:

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 

W ten sposób mógłbym mieć wiele instrukcji wewnątrz pętli while. Moja klasa Visitor wygląda następująco:

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); 
} 

}

Większość kodu w klasie zwiedzających wziąłem z książki, bo jestem dopiero zaczyna używając ANTLR 4. Trudno mi uwierzyć, że na bok z gramatyki pętli while, nie ma żadnej wzmianki o tym, jak zaimplementować użytkowników/słuchaczy lub akcje dla prostej pętli While w książce napisanej przez Terence'a Parra. Czy ktoś może mi pomóc w pisaniu kodu Java dla odwiedzających/słuchaczy dla pętli While?

+0

Witam, to jest więcej treści z "Wzorców implementacji języka": http: //www.amazon .pl/Language-Implementation-Patterns-Domain-specific-Programming/dp/193435645X/ref = pd_bxgy_b_img_y –

+0

@ TheANTLRGuy Mam tę książkę i rzuciłem okiem na nią, aby sprawdzić, czy mogę znaleźć sposób na wykonanie pętli while i Niestety, tak nie jest. – Greg

+0

Czy spojrzałeś na wzorzec zwany interpreter drzewa? Strona 230. nie robi tego specjalnie podczas pętli, ale to sprawia, że ​​powrót Colina jest znacznie bardziej skomplikowany. Wyraźnie pokazuje, jak przenieść licznik wirtualnego programu przez drzewo. –

Odpowiedz

10

trudno mi uwierzyć, że oprócz gramatyki dla pętli while, nie ma wzmianki o tym, jak wdrożyć żadnych interesantów/słuchaczy lub działania na prostą Podczas pętli w książce napisanej przez Terence Parr.

To dlatego, że chodzi o odniesienie ANTLR antlr, co stanowi około parsowania, a nie o scenie po parsowania.

Nie możesz tego zrobić:

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

Już ogłosił gościem powrotu Integer s, więc to, co każda reguła powinna wrócić. Albo zrobić niestandardowy otoki Value która zamyka wartości swój język'S (numery, łańcuch, wartości logicznych), lub po prostu wrócić 0 gdy wyrażenie relacja jest fałszywa:

@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; 
    } 
} 

Następnie można napisać while następująco:

@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; 
} 

Należy zauważyć, że powyższy kod nie powiedzie się podczas zagnieżdżania instrukcji while, ponieważ wewnętrzny powróciłby 0, powodując, że zewnętrzna pętla przestanie się obracać. W takich przypadkach lepiej byłoby utworzyć niestandardową klasę Value i wprowadzić instancję typu Value.VOID, która nie spowoduje zatrzymania pętli.

uruchamiając następujące główne metody:

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()); 
} 

będzie druk:

1 
2 
3 
4 
5 
6 
7 
8 
9

mają również spojrzeć na ten język demo, który wykorzystuje ANTLR4 i if i while sprawozdań, a także zwyczaj Value obiekt: https://github.com/bkiers/Mu

+0

Dziękuję bardzo, Bart. Twoje rozwiązanie działa cuda! Jedno pytanie jednak - staram się owijać głowę o tym, co robi się w metodach odwiedzających, które sprawiają, że wszystko to działa. Weźmy na przykład implementację instrukcji if: co robisz, to gromadzenie wszystkich warunków + ich bloków kodu na liście. Następnie sprawdzasz, które warunki są spełnione, abyś mógł następnie przejść do bloku instrukcji odpowiadającego temu konkretnemu warunkowi - dlatego połączyłeś swój blok warunku + instrukcji, aby łatwo zlokalizować blok, który powinien zostać uruchomiony. Mam rację? – Greg

+0

@Greg, tak, to prawda. –

+0

Jeszcze raz dziękuję Bart :) – Greg