Грамматика для распознавания неограниченного количества {{expr '}' рядом друг с другом

Я пишу приложение на C# с использованием ANTLR4 для распознавания следующего стиля TeX'ish:

{А} {х} + {Ь} {у} + {C}

Моя текущая грамматика всегда берет последний экземпляр '{' expr '}', а затем игнорирует начало строки. Вот некоторые выходные результаты текущей грамматики (описанные ниже):

  • Вход: {a} Выход: a [Pass]
  • Вход: {a} + {x} Выход: a + x [Pass]
  • Вход: {a} {x} Выход: x [Fail] Требуется: ax
  • Входные данные: {a} {x} + {b} Выходные данные: x + b [Fail] Желаемый: ax + b
  • Входные данные: {a} {x} + {b} {y} Выходные данные: y [Fail] Желаемый: ax + by
  • Входные данные: {a} {x} + {b} {y} + {c} Выходные данные: y + c [Fail] Желаемые: ax + by + c
  • Входные данные: {a} {x} + {b} {y} + {c} {d} Выходные данные: d [Fail] Желаемый: ax + by + cd

Любые идеи о том, как это исправить?

Файл грамматики MyGra.g4:

/*
 * Parser Rules
 */
prog: expr+ ;

expr : '{' expr '}'                 # CB_Expr
     | expr op=('+'|'-') expr       # AddSub
     | '{' ID '}'                   # CB_ID
     | ID                           # ID
     ;

/*
 * Lexer Rules
 */
ID: ('a' .. 'z' | 'A' .. 'Z')+;
ADD : '+';
SUB : '-';
WS:   (' ' | '\r' | '\n') -> channel(HIDDEN);

Файл MyGraVisitor.CS:

 public override string VisitID(MyGraParser.IDContext context)
 {
      return context.ID().GetText();
 }

 public override string VisitAddSub(MyGraParser.AddSubContext context)
 {
     if (context.op.Type == MyGraParser.ADD)
     {
         return Visit(context.expr(0)) + " + " + Visit(context.expr(1));
     }
     else
     {
         return Visit(context.expr(0)) + " - " + Visit(context.expr(1));
     }
 }

 public override string VisitCB_Expr(MyGraParser.CB_ExprContext context)
 {
     return Visit(context.expr());
 }

 public override string VisitCB_ID(MyGraParser.CB_IDContext context)
 {
     return context.ID().GetText();
 }

Обновление № 1:

Было предложено включить грамматическое правило для

'{' expr '}{' expr '}'

однако, что если у меня есть {a} {b} {c} {d} + {e} {f} {g}, я думал, что грамматика должна была учитывать рекурсивные версии "себя" через деревья разбора... так что если у меня есть 1000 {expr} рядом друг с другом? Сколько правил мне нужно тогда? Я думаю, что предложение действительно, за исключением того, что я не уверен, как учитывать неограниченное количество {expr} рядом друг с другом?

У меня есть еще один вопрос: как я могу повторно использовать правило CB_Expr?

Обновление № 2:

Я добавил правило:

| expr CB_Expr                  # CB_Expr2

с посетителем:

public override string VisitCB_Expr2(MyGra.CB_Expr2Context context)
{
    return Visit(context.expr()) + Visit(context.CB_Expr());
}

Это не помогло, я все равно получаю одинаковый вывод для всех случаев (описанных выше).

1 ответ

Решение

Ваша грамматика неоднозначна. Например: вход {x} может иметь два разных дерева разбора (как сказал Мефи):

(CB_Expr {(expr (ID x))})

а также

(DB_ID {x})

Удаление CB_ID исправило бы это, фактически не делая ничего отрицательного.

Для вашей реальной проблемы, это должно сработать для expr:

expr : left=id_expr op=('+' |'-') right=expr #AddSub
     | id_expr                               #ID_Expr
     ;

id_expr :
     | '{' ID '}' id_expr                    #ID_Ex
     | '{' ID '}'                            #ID
     ;

Я не проверял это, хотя, и я не написал вам посетителей, но грамматика должна работать.

Правило id_expr работает рекурсивно, поэтому вы должны иметь возможность ставить столько {ID} друг за другом, сколько захотите - хотя бы одно, хотя грамматика сейчас такова.

Другие вопросы по тегам