Дана контекстно-свободная грамматика, порождающая арифметические выражения с левой рекурсией: E -> E + T | T; T -> T * F | F; F -> (E) | id — уберите левую рекурсию и преобразуйте грамматику в форму, пригодную для построения рекурсивного нисходящего парсера
Начальная грамматика: E→E+T∣T;T→T∗F∣F;F→(E)∣id
E \to E + T \mid T;\quad T \to T * F \mid F;\quad F \to (E) \mid id E→E+T∣T;T→T∗F∣F;F→(E)∣id Правило устранения немедленной левой рекурсии: для формы A→Aα∣βA \to A\alpha \mid \betaA→Aα∣β заменить на A→βA′;A′→αA′∣ε
A \to \beta A';\qquad A' \to \alpha A' \mid \varepsilon A→βA′;A′→αA′∣ε Применяя это к EEE и TTT, получаем грамматику, пригодную для рекурсивного нисходящего парсера: E→T E′
E \to T\ E' E→TE′E′→+ T E′∣ε
E' \to +\ T\ E' \mid \varepsilon E′→+TE′∣εT→F T′
T \to F\ T' T→FT′T′→∗ F T′∣ε
T' \to *\ F\ T' \mid \varepsilon T′→∗FT′∣εF→( E )∣id
F \to (\,E\,) \mid id F→(E)∣id Краткое пояснение: левой рекурсии нет, порядок правил сохраняет приоритеты операций (∗*∗ выше +++). Для реализации в рекурсивном нисходящем парсере удобно реализовать E′E'E′ и T′T'T′ как циклы: например parseE(): - node = parseT() - while lookahead == '+': consume('+'); rhs = parseT(); node = makePlus(node, rhs) - return node parseT() аналогично для '*'. Эта грамматика пригодна для рекурсивного нисходящего парсера и позволяет построить левоассоциативные суммы и произведения.
E→E+T∣T;T→T∗F∣F;F→(E)∣id E \to E + T \mid T;\quad T \to T * F \mid F;\quad F \to (E) \mid id
E→E+T∣T;T→T∗F∣F;F→(E)∣id
Правило устранения немедленной левой рекурсии: для формы A→Aα∣βA \to A\alpha \mid \betaA→Aα∣β заменить на
A→βA′;A′→αA′∣ε A \to \beta A';\qquad A' \to \alpha A' \mid \varepsilon
A→βA′;A′→αA′∣ε
Применяя это к EEE и TTT, получаем грамматику, пригодную для рекурсивного нисходящего парсера:
E→T E′ E \to T\ E'
E→T E′ E′→+ T E′∣ε E' \to +\ T\ E' \mid \varepsilon
E′→+ T E′∣ε T→F T′ T \to F\ T'
T→F T′ T′→∗ F T′∣ε T' \to *\ F\ T' \mid \varepsilon
T′→∗ F T′∣ε F→( E )∣id F \to (\,E\,) \mid id
F→(E)∣id
Краткое пояснение: левой рекурсии нет, порядок правил сохраняет приоритеты операций (∗*∗ выше +++). Для реализации в рекурсивном нисходящем парсере удобно реализовать E′E'E′ и T′T'T′ как циклы: например
parseE():
- node = parseT()
- while lookahead == '+': consume('+'); rhs = parseT(); node = makePlus(node, rhs)
- return node
parseT() аналогично для '*'.
Эта грамматика пригодна для рекурсивного нисходящего парсера и позволяет построить левоассоциативные суммы и произведения.