Невозможно проанализировать строку с символами с помощью ANTLR 4

#parsing #antlr #antlr4

#синтаксический анализ #antlr #antlr4

Вопрос:

Я настроил грамматику простых выражений, найденную в книге «The Definitive ANTLR 4 Reference». Новая грамматика выглядит следующим образом:

 grammar Expr; 

prog:   stat  ;

stat:   expr NEWLINE                    # printExpr
    |   ID '=' expr NEWLINE             # assign
    |   NEWLINE                         # blank
    ;

expr:   expr op=('*'|'/') expr          # MulDiv
    |   expr op=(' '|'-') expr          # AddSub
    |   INT                             # int
    |   ID                              # id
    |   '(' expr ')'                    # parens
    |   'min' '(' expr ',' expr ')'     # Min    
    |   'max' '(' expr ',' expr ')'     # Max
    |   'len' '(' string_constant ')'   # Len
    ;

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

string_constant :  '"' (ESC | ~('"' | '\') )* '"' ;

ESC :   '\' (["\/bfnrt] | UNICODE) ;
fragment UNICODE : 'u' HEX HEX HEX HEX ;
fragment HEX : [0-9a-fA-F] ;
 

Есть пара новых математических функций (min и max) и длина строки.
Здесь есть посетитель дерева, который я использовал для анализа:

 public class EvalVisitor extends ExprBaseVisitor<Integer> 
{
    /** "memory" for our calculator; variable/value pairs go here */
    Map<String, Integer> 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 = 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 = 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 = visit(ctx.expr(0));  // get value of left subexpression
        int right = 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 = visit(ctx.expr(0));  // get value of left subexpression
        int right = 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 visit(ctx.expr()); }

    /** 'min' '(' expr ',' expr ')' */
    @Override public Integer visitMin(@NotNull ExprParser.MinContext ctx) 
    { 
        int left = visit(ctx.expr(0));  // get value of left subexpression
        int right = visit(ctx.expr(1)); // get value of right subexpression
        return Math.min(left,  right);
    }

    /** 'max' '(' expr ',' expr ')' */
    @Override public Integer visitMax(@NotNull ExprParser.MaxContext ctx) 
    { 
        int left = visit(ctx.expr(0));  // get value of left subexpression
        int right = visit(ctx.expr(1)); // get value of right subexpression
        return Math.max(left,  right);
    }

    /** 'len' '(' string_constant ')' */
    @Override public Integer visitLen(@NotNull ExprParser.LenContext ctx) 
    { 
        String str = ctx.string_constant().getText();
        return str.length()-2;
    }
}
 

Посетитель может проанализировать выражение:

    len("hello")
 

Но не может проанализировать выражение:

    len("hello%")
 

Полученное сообщение выглядит следующим образом:

 > java -jar Calc.jar 
len("hello")
len("hello%")
line 2:10 token recognition error at: '%'
5
5
 

Есть ли какая-либо ошибка в определении string_constant ?

С уважением, Йона (than)

Ответ №1:

string_constant вместо этого должно быть правило лексера:

 STRING_CONSTANT
 :  '"' ( ESC | ~('"' | '\') )* '"' 
 ;
 

и ESC fragment :

 fragment ESC : '\' (["\/bfnrt] | UNICODE) ;
 

Комментарии:

1. Спасибо, Барт, с предложенным изменением все работает.