Постройте формальную грамматику (контекстно‑свобо́дную или регулярную) для описания простого языка арифметических выражений с унарным минусом, скобками и операциями +, -, *, /; объясните, как преобразовать эту грамматику в парсер (рекурсивный‑нисходящий или табличный) и укажите возможные проблемы леворекурсии или неоднозначности

29 Окт в 09:25
4 +4
0
Ответы
1
Грамматика (контекстно‑свободная, LL(1)-совместимая, учитывает унарный минус и приоритеты):
Expr → Term Expr’Expr’ → ′+′ Term Expr’ ∣ ′−′ Term Expr’ ∣ ϵTerm → Factor Term’Term’ → ′∗′ Factor Term’ ∣ ′/′ Factor Term’ ∣ ϵFactor → ′−′ Factor ∣ ′(′ Expr ′)′ ∣ NUMBER \begin{aligned}
&\text{Expr} \;\to\; \text{Term}\ \text{Expr'}\\
&\text{Expr'} \;\to\; '+'\ \text{Term}\ \text{Expr'} \;|\; '-'\ \text{Term}\ \text{Expr'} \;|\; \epsilon\\
&\text{Term} \;\to\; \text{Factor}\ \text{Term'}\\
&\text{Term'} \;\to\; '*'\ \text{Factor}\ \text{Term'} \;|\; '/'\ \text{Factor}\ \text{Term'} \;|\; \epsilon\\
&\text{Factor} \;\to\; '-'\ \text{Factor} \;|\; '('\ \text{Expr}\ ')'\;|\; \text{NUMBER}
\end{aligned}
ExprTerm Expr’Expr’+ Term Expr’ Term Expr’ϵTermFactor Term’Term’ Factor Term’/ Factor Term’ϵFactor Factor( Expr )NUMBER

Пояснения:
- Приоритеты заданы структурой: Factor\text{Factor}Factor (унарный минус, скобки, число) имеет наибольший приоритет, затем ∗,/*,/,/, затем +,−+,-+,.
- Унарный минус реализован правилом Factor→′−′ Factor\text{Factor}\to '-'\ \text{Factor}Factor Factor, что делает его правооперавативным: −−a- -aa парсится как −(−a)-(-a)(a).
- ϵ\epsilonϵ — пустая продукция.
Как преобразовать в парсер
1) Рекурсивный нисходящий (recursive‑descent, прямой код):
- Для каждой нетерминальной функции написать процедуру, использующую текущее токен/лексему.
- Пример псевдокода (очень сжато):
parseExpr():
parseTerm()
while currentToken in {'+','-'}:
op = consume()
parseTerm()
buildBinary(op)
parseTerm():
parseFactor()
while currentToken in {'*','/'}:
op = consume()
parseFactor()
buildBinary(op)
parseFactor():
if currentToken == '-':
consume()
node = parseFactor()
return makeUnary('-', node)
elif currentToken == '(':
consume(); node = parseExpr(); expect(')'); return node
elif currentToken is NUMBER:
return makeNumber(consume())
- Эта версия использует устранённую левую рекурсию (см. ниже) и является простым LL(1)-парсером при корректной лексеме NUMBER и разделении токенов.
2) Табличный LL(1)-парсер:
- Построить FIRST/FOLLOW для нетерминалов и таблицу разборов; таблица будет однозначна для предложенной грамматики (при корректном лексическом разборе).
- Алгоритм: стек с начальным символом Expr\text{Expr}Expr и входной поток токенов; применять продукцию по таблице.
Возможные проблемы
- Леворекурсия: простая запись вида Expr→Expr+Term\text{Expr}\to\text{Expr} + \text{Term}ExprExpr+Term является лево-рекурсивной и не подходит для рекурсивного нисходящего парсера. Решение — факторизация/преобразование в форму с хвостовой продукцией (как в приведённой грамматике с Expr’\text{Expr'}Expr’ и Term’\text{Term'}Term’).
- Неоднозначность: грамматика вида Expr→Expr ′+′ Expr ∣ Expr ′∗′Expr ∣ …\text{Expr}\to\text{Expr}\ '+'\ \text{Expr}\ |\ \text{Expr}\ '*' \text{Expr}\ |\ \dotsExprExpr + Expr Expr Expr без явного задания приоритетов/ассоциативности будет неоднозначна. В приведённой грамматике приоритеты и левосторонняя ассоциативность для +,−+,-+, и ∗,/*,/,/ закодированы структурой (она не неоднозначна).
- Унарный vs бинарный минус: конфликт решается грамматикой контекстно (в позициях начала факторa минус трактуется как унарный, в позиции после терма — как бинарный в Expr’\text{Expr'}Expr’). В лексере/парсере надо отличать только токен '-'; грамматика и алгоритм разбора однозначно определяют роль.
- LR‑парсер: для bottom‑up парсеров возможны shift/reduce конфликты для грамматик с неявными приоритетами; решается либо явным определением приоритетов/ассоциативности, либо использованием той же факторизованной грамматики.
- Error‑recovery и лексические детали (пробелы, целые/вещественные числа, идентификаторы) нужно реализовать отдельно.
Итого: использовать предложенную LL(1)-грамматику, реализовать рекурсивный нисходящий парсер по шаблону parseExpr/parseTerm/parseFactor; избегать исходной леворекурсивной формы и неявных приоритетов, чтобы устранить неоднозначности.
29 Окт в 10:20
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир