Система Yacc
В основу системы Yacc положен синтаксический анализатор типа LALR(1), генерируемый по входной (мета) программе. Эта программа
состоит из трех частей:
%{ Си-текст %} %token Список имен лексем %% Список правил трансляции %% Служебные Си-подпрограммы |
Си-текст (который вместе с окружающими скобками %{ и %} может отсутствовать) обычно содержит Си-объявления (включая #include и #define), используемые в тексте ниже. Этот Си-текст может содержать и объявления (или предобъявления) функций.
Список имен лексем содержит имена, которые преобразуются Yacc-препроцессором в объявления констант (#define). Как правило, эти имена используются
как имена классов лексем и служат для определения интерфейса с лексическим анализатором.
Каждое правило трансляции имеет вид
Левая_часть : альтернатива_1 {семантические_действия_1} | альтернатива_2 {семантические_действия_2} |... | альтернатива_n {семантические_действия_n} ; |
Каждое семантическое действие - это последовательность операторов Си. При этом каждому нетерминалу может быть сопоставлен один синтезируемый атрибут. На атрибут нетерминала левой части ссылка осуществляется посредством значка $$, на атрибуты символов правой части - посредством значков $1, $2 , ..., $n, причем номер соответствует порядку элементов правой части, включая
семантические действия. Каждое семантическое действие может вырабатывать значение в результате выполнения присваивания $$=Выражение. Исполнение текого оператора в последнем семантическом действии определяет значение атрибута символа левой части.
В некоторых случаях допускается использование грамматик, имеющих конфликты. При этом синтаксический анализатор разрешает конфликты следующим образом:
- конфликты типа свертка/ свертка разрешаются выбором правила, предшествующего во входной
метапрограмме;
- конфликты типа сдвиг/свертка разрешаются предпочтением сдвига. Поскольку этих правил не всегда достаточно для правильного определения анализатора, допускается определение старшинства и ассоциативности терминалов.
Например, объявление
%left '+' '-' |
%right '^' |
%nonassoc ' |
старшинства s или если старшинство одинаково, а правило левоассоциативно. В противном случае делается сдвиг.
Обычно за старшинство правила принимается старшинство самого правого терминала правила. В тех случаях, когда самый правый терминал не дает нужного приоритета, этот приоритет можно назначить следующим объявлением:
%prec терминал |
Yacc не сообщает о конфликтах, разрешаемых с помощью ассоциативности и приоритетов. Восстановление после ошибок управляется пользователем с помощью введения в грамматику «правил ошибки» вида
A error w.
Здесь error - ключевое слово Yacc. Когда встречается синтаксическая ошибка, анализатор трактует состояние, набор ситуаций
для которого содержит правило для error, некоторым специальным образом: символы из стека выталкиваются до тех пор, пока на верхушке стека не будет обнаружено состояние, для которого набор ситуаций содержит ситуацию вида [A .error w].После чего в стек фиктивно помещается символ error, как если бы он встретился во входной строке.
Если w пусто, делается свертка. После этого анализатор пропускает входные символы, пока не найдет такой, с которым
можно продолжить нормальный разбор.
Если w не пусто, просматривается входная строка и делается попытка свернуть w. Если w состоит только из терминалов, эта строка ищется во входном потоке.