Постройте контекстно-свободную грамматику, описывающую простую арифметическую подсистема с приоритетом операций и скобками; проанализируйте возможную неоднозначность и способы её устранения
Грамматика с явными уровнями приоритета и ассоциативности (неоднозначность устранена структурой правил): Нетерминалы: Expr, Term, Unary, Power, Primary\text{Expr},\ \text{Term},\ \text{Unary},\ \text{Power},\ \text{Primary}Expr,Term,Unary,Power,Primary. Терминалы: + −, ∗, /, , (, ), number, id+\,-,\ *,\ /,\ ^,\ (,\ ),\ \text{number},\ \text{id}+−,∗,/,,(,),number,id. Грамматика (неоднозначная форма с управлением приоритетами и ассоциативностью): Expr→Expr+Term ∣ Expr−Term ∣ TermTerm→Term∗Power ∣ Term/Power ∣ PowerPower→PrimaryPower ∣ PrimaryPrimary→( Expr ) ∣ number ∣ id ∣ +Primary ∣ −Primary
\begin{aligned} \text{Expr} &\to \text{Expr} + \text{Term}\ \mid\ \text{Expr} - \text{Term}\ \mid\ \text{Term}\\ \text{Term} &\to \text{Term} * \text{Power}\ \mid\ \text{Term} / \text{Power}\ \mid\ \text{Power}\\ \text{Power} &\to \text{Primary} ^ \text{Power}\ \mid\ \text{Primary}\\ \text{Primary} &\to (\,\text{Expr}\,)\ \mid\ \text{number}\ \mid\ \text{id}\ \mid\ +\text{Primary}\ \mid\ -\text{Primary} \end{aligned} ExprTermPowerPrimary→Expr+Term∣Expr−Term∣Term→Term∗Power∣Term/Power∣Power→PrimaryPower∣Primary→(Expr)∣number∣id∣+Primary∣−Primary Объяснение выбора: - Сложение/вычитание реализованы лево-рекурсивно в Expr\text{Expr}Expr → это обеспечивает левую ассоциативность для +++ и −-− (например, 1−2−3\;1-2-31−2−3 парсится как (1−2)−3(1-2)-3(1−2)−3). - Умножение/деление аналогично в Term\text{Term}Term — более высокий приоритет, также левая ассоциативность. - Возведение в степень через правую рекурсию в Power\text{Power}Power (PrimaryPower\text{Primary} ^ \text{Power}PrimaryPower) даёт правую ассоциативность для \(^\) (например, 234\;2^{3^{4}}234 означает 2(34)2^{(3^{4})}2(34)). - Унарные +++ и −-− включены в Primary\text{Primary}Primary или можно вынести в отдельный уровень, в зависимости от желаемого отношения с \(^\): в приведённой грамматике \(^\) имеет более высокий приоритет чем унарный (то есть −22-2^2−22 парсится как −(22)-(2^2)−(22)). (Если нужно, чтобы унарный был сильнее \(^\), поменять порядок уровней.) Анализ неоднозначности и способы её устранения: - Неоднозначность возникает, если использовать шаблонную продукцию вида E→E op E∣(E)∣number\text{E}\to\text{E op E}\mid(\text{E})\mid\text{number}E→E op E∣(E)∣number: одна и та же строка имеет много деревьев (нет заданных приоритетов/ассоциативности). - Устранить неоднозначность можно: 1. Разделить грамматику на уровни по приоритету (как выше): каждый уровень — отдельный нетерминал. 2. Задать ассоциативность через направление рекурсии: левая рекурсия → левая ассоциативность; правая рекурсия → правая ассоциативность. 3. Для генераторов/парсеров (Yacc/Bison) использовать директивы приоритета/ассоциативности (%left,\ %right,\ %nonassoc). 4. Применять алгоритмы разбора с учётом приоритетов (precedence climbing, Pratt parser) — не меняют грамматику, но разрешают конфликты при разборе. - Особые замечания по унарному минусу и степеням: соглашение о том, что сильнее — унарный оператор или степень, должно быть явно отражено в грамматике (изменением уровня для унарного оператора), иначе выражения вроде −22-2^2−22 будут интерпретироваться по-разному. Пример LL(1)-дружественной формы (без левой рекурсии, эквивалент по приоритетам): Expr→Term Expr’Expr’→+ Term Expr’ ∣ − Term Expr’ ∣ εTerm→Power Term’Term’→∗ Power Term’ ∣ / Power Term’ ∣ εPower→Unary Power’Power’→ Power ∣ εUnary→+ Unary ∣ − Unary ∣ PrimaryPrimary→( Expr ) ∣ number ∣ id
\begin{aligned} \text{Expr} &\to \text{Term}\ \text{Expr'}\\ \text{Expr'} &\to +\ \text{Term}\ \text{Expr'}\ \mid\ -\ \text{Term}\ \text{Expr'}\ \mid\ \varepsilon\\ \text{Term} &\to \text{Power}\ \text{Term'}\\ \text{Term'} &\to *\ \text{Power}\ \text{Term'}\ \mid\ /\ \text{Power}\ \text{Term'}\ \mid\ \varepsilon\\ \text{Power} &\to \text{Unary}\ \text{Power'}\\ \text{Power'} &\to ^\ \text{Power}\ \mid\ \varepsilon\\ \text{Unary} &\to +\ \text{Unary}\ \mid\ -\ \text{Unary}\ \mid\ \text{Primary}\\ \text{Primary} &\to (\,\text{Expr}\,)\ \mid\ \text{number}\ \mid\ \text{id} \end{aligned} ExprExpr’TermTerm’PowerPower’UnaryPrimary→TermExpr’→+TermExpr’∣−TermExpr’∣ε→PowerTerm’→∗PowerTerm’∣/PowerTerm’∣ε→UnaryPower’→Power∣ε→+Unary∣−Unary∣Primary→(Expr)∣number∣id Эта форма однозначна и пригодна для реализации в рекурсивном нисходящем парсере или в LL(1)-анализаторе. Кратко: используйте уровни нетерминалов для приоритета и направление рекурсии для ассоциативности; при помощи этого грамматика становится однозначной.
Нетерминалы: Expr, Term, Unary, Power, Primary\text{Expr},\ \text{Term},\ \text{Unary},\ \text{Power},\ \text{Primary}Expr, Term, Unary, Power, Primary.
Терминалы: + −, ∗, /, , (, ), number, id+\,-,\ *,\ /,\ ^,\ (,\ ),\ \text{number},\ \text{id}+−, ∗, /, , (, ), number, id.
Грамматика (неоднозначная форма с управлением приоритетами и ассоциативностью):
Expr→Expr+Term ∣ Expr−Term ∣ TermTerm→Term∗Power ∣ Term/Power ∣ PowerPower→PrimaryPower ∣ PrimaryPrimary→( Expr ) ∣ number ∣ id ∣ +Primary ∣ −Primary \begin{aligned}
\text{Expr} &\to \text{Expr} + \text{Term}\ \mid\ \text{Expr} - \text{Term}\ \mid\ \text{Term}\\
\text{Term} &\to \text{Term} * \text{Power}\ \mid\ \text{Term} / \text{Power}\ \mid\ \text{Power}\\
\text{Power} &\to \text{Primary} ^ \text{Power}\ \mid\ \text{Primary}\\
\text{Primary} &\to (\,\text{Expr}\,)\ \mid\ \text{number}\ \mid\ \text{id}\ \mid\ +\text{Primary}\ \mid\ -\text{Primary}
\end{aligned}
ExprTermPowerPrimary →Expr+Term ∣ Expr−Term ∣ Term→Term∗Power ∣ Term/Power ∣ Power→PrimaryPower ∣ Primary→(Expr) ∣ number ∣ id ∣ +Primary ∣ −Primary
Объяснение выбора:
- Сложение/вычитание реализованы лево-рекурсивно в Expr\text{Expr}Expr → это обеспечивает левую ассоциативность для +++ и −-− (например, 1−2−3\;1-2-31−2−3 парсится как (1−2)−3(1-2)-3(1−2)−3).
- Умножение/деление аналогично в Term\text{Term}Term — более высокий приоритет, также левая ассоциативность.
- Возведение в степень через правую рекурсию в Power\text{Power}Power (PrimaryPower\text{Primary} ^ \text{Power}PrimaryPower) даёт правую ассоциативность для \(^\) (например, 234\;2^{3^{4}}234 означает 2(34)2^{(3^{4})}2(34)).
- Унарные +++ и −-− включены в Primary\text{Primary}Primary или можно вынести в отдельный уровень, в зависимости от желаемого отношения с \(^\): в приведённой грамматике \(^\) имеет более высокий приоритет чем унарный (то есть −22-2^2−22 парсится как −(22)-(2^2)−(22)). (Если нужно, чтобы унарный был сильнее \(^\), поменять порядок уровней.)
Анализ неоднозначности и способы её устранения:
- Неоднозначность возникает, если использовать шаблонную продукцию вида E→E op E∣(E)∣number\text{E}\to\text{E op E}\mid(\text{E})\mid\text{number}E→E op E∣(E)∣number: одна и та же строка имеет много деревьев (нет заданных приоритетов/ассоциативности).
- Устранить неоднозначность можно:
1. Разделить грамматику на уровни по приоритету (как выше): каждый уровень — отдельный нетерминал.
2. Задать ассоциативность через направление рекурсии: левая рекурсия → левая ассоциативность; правая рекурсия → правая ассоциативность.
3. Для генераторов/парсеров (Yacc/Bison) использовать директивы приоритета/ассоциативности (%left,\ %right,\ %nonassoc).
4. Применять алгоритмы разбора с учётом приоритетов (precedence climbing, Pratt parser) — не меняют грамматику, но разрешают конфликты при разборе.
- Особые замечания по унарному минусу и степеням: соглашение о том, что сильнее — унарный оператор или степень, должно быть явно отражено в грамматике (изменением уровня для унарного оператора), иначе выражения вроде −22-2^2−22 будут интерпретироваться по-разному.
Пример LL(1)-дружественной формы (без левой рекурсии, эквивалент по приоритетам):
Expr→Term Expr’Expr’→+ Term Expr’ ∣ − Term Expr’ ∣ εTerm→Power Term’Term’→∗ Power Term’ ∣ / Power Term’ ∣ εPower→Unary Power’Power’→ Power ∣ εUnary→+ Unary ∣ − Unary ∣ PrimaryPrimary→( Expr ) ∣ number ∣ id \begin{aligned}
\text{Expr} &\to \text{Term}\ \text{Expr'}\\
\text{Expr'} &\to +\ \text{Term}\ \text{Expr'}\ \mid\ -\ \text{Term}\ \text{Expr'}\ \mid\ \varepsilon\\
\text{Term} &\to \text{Power}\ \text{Term'}\\
\text{Term'} &\to *\ \text{Power}\ \text{Term'}\ \mid\ /\ \text{Power}\ \text{Term'}\ \mid\ \varepsilon\\
\text{Power} &\to \text{Unary}\ \text{Power'}\\
\text{Power'} &\to ^\ \text{Power}\ \mid\ \varepsilon\\
\text{Unary} &\to +\ \text{Unary}\ \mid\ -\ \text{Unary}\ \mid\ \text{Primary}\\
\text{Primary} &\to (\,\text{Expr}\,)\ \mid\ \text{number}\ \mid\ \text{id}
\end{aligned}
ExprExpr’TermTerm’PowerPower’UnaryPrimary →Term Expr’→+ Term Expr’ ∣ − Term Expr’ ∣ ε→Power Term’→∗ Power Term’ ∣ / Power Term’ ∣ ε→Unary Power’→ Power ∣ ε→+ Unary ∣ − Unary ∣ Primary→(Expr) ∣ number ∣ id
Эта форма однозначна и пригодна для реализации в рекурсивном нисходящем парсере или в LL(1)-анализаторе.
Кратко: используйте уровни нетерминалов для приоритета и направление рекурсии для ассоциативности; при помощи этого грамматика становится однозначной.