Грамматика для распознавания неограниченного количества {{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} друг за другом, сколько захотите - хотя бы одно, хотя грамматика сейчас такова.