Дана контекстно-свободная грамматика G с продукциями: S -> S S | "a" | "b"; покажите, что грамматика неоднозначна, приведите конкретную строку с двумя различными деревьями разбора, и предложите способ модификации грамматики или использования ограничений для получения неоднозначности-устойчивого синтаксического анализа
Грамматика S -> S S | "a" | "b" неоднозначна. Интуитивно: S S — это бинарная операция конкатенации, и любую строку из n терминалов можно разбить на бинарную скобочную структуру по-разному (число структур равно числу каталановых деревьев), поэтому для n ≥ 3 существуют ≥ 2 различных деревьев разбора.
Конкретный пример. Возьмём строку "aaa". Два различных дерева разбора:
1) лево-ассоциативное ((a a) a): S ├─ S │ ├─ S │ │ └─ "a" │ └─ "a" └─ "a"
или в виде вывода: S ⇒ S S ⇒ S S S ⇒ "a" S S ⇒ "a" "a" S ⇒ "a" "a" "a"
2) право-ассоциативное (a (a a)): S ├─ "a" └─ S ├─ "a" └─ "a"
или в виде вывода: S ⇒ S S ⇒ S S S ⇒ S "a" S ⇒ "a" "a" "a"
Оба дерева дают одну и ту же терминальную строку "aaa", но имеют разную структуру — значит грамматика неоднозначна.
Как получить устойчивый (неоднозначности-устойчивый) разбор
Сделать грамматику неамбигуной, явно задав ассоциативность (список) вместо бинарной рекурсии: Например, описать непустую последовательность терминалов однозначно: S -> A | A S A -> "a" | "b" Эта грамматика порождает те же строки (все непустые строки над {a,b}), но даёт однозначный (право-рекурсивный) формат разбора — вся последовательность имеет фиксированное правое (или если нужно, левое) вложение.
Использовать н-арную конструкцию вместо бинарной: в семантике/AST хранить конкатенацию как n-арный узел (список терминалов/поддеревьев), и при разборе «свернуть» любые бинарные деревья в один список. Тогда разные бинарные деревья считаются эквивалентными и приводят к одному AST.
В парсерах/генераторах объявить ассоциативность и приоритет (например, declare concatenation как левую или правую ассоциативную операцию). Тогда парсер разрешит неоднозначность в одну сторону.
Использовать ограничения/семантические проверки: если грамматика остаётся амбигуной, можно обнаруживать неоднозначные разборы (в GLR/Earley) и отбрасывать все, кроме подходящих по семантике (например, по дополнительным атрибутам).
Коротко: простейший путь — заменить S -> S S | "a" | "b" на списокную форму S -> A | A S (или S -> S A | A), либо при построении AST сворачивать конкатенации в список. Тогда разбор будет однозначным.
Грамматика
S -> S S | "a" | "b"
неоднозначна. Интуитивно: S S — это бинарная операция конкатенации, и любую строку из n терминалов можно разбить на бинарную скобочную структуру по-разному (число структур равно числу каталановых деревьев), поэтому для n ≥ 3 существуют ≥ 2 различных деревьев разбора.
Конкретный пример. Возьмём строку "aaa". Два различных дерева разбора:
1) лево-ассоциативное ((a a) a):
S
├─ S
│ ├─ S
│ │ └─ "a"
│ └─ "a"
└─ "a"
или в виде вывода:
S ⇒ S S ⇒ S S S ⇒ "a" S S ⇒ "a" "a" S ⇒ "a" "a" "a"
2) право-ассоциативное (a (a a)):
S
├─ "a"
└─ S
├─ "a"
└─ "a"
или в виде вывода:
S ⇒ S S ⇒ S S S ⇒ S "a" S ⇒ "a" "a" "a"
Оба дерева дают одну и ту же терминальную строку "aaa", но имеют разную структуру — значит грамматика неоднозначна.
Как получить устойчивый (неоднозначности-устойчивый) разбор
Сделать грамматику неамбигуной, явно задав ассоциативность (список) вместо бинарной рекурсии:
Например, описать непустую последовательность терминалов однозначно:
S -> A | A S
A -> "a" | "b"
Эта грамматика порождает те же строки (все непустые строки над {a,b}), но даёт однозначный (право-рекурсивный) формат разбора — вся последовательность имеет фиксированное правое (или если нужно, левое) вложение.
Использовать н-арную конструкцию вместо бинарной: в семантике/AST хранить конкатенацию как n-арный узел (список терминалов/поддеревьев), и при разборе «свернуть» любые бинарные деревья в один список. Тогда разные бинарные деревья считаются эквивалентными и приводят к одному AST.
В парсерах/генераторах объявить ассоциативность и приоритет (например, declare concatenation как левую или правую ассоциативную операцию). Тогда парсер разрешит неоднозначность в одну сторону.
Использовать ограничения/семантические проверки: если грамматика остаётся амбигуной, можно обнаруживать неоднозначные разборы (в GLR/Earley) и отбрасывать все, кроме подходящих по семантике (например, по дополнительным атрибутам).
Коротко: простейший путь — заменить S -> S S | "a" | "b" на списокную форму S -> A | A S (или S -> S A | A), либо при построении AST сворачивать конкатенации в список. Тогда разбор будет однозначным.