Изучение бизонов: что такое контекстно-свободные грамматики и LALR (1)? - PullRequest
6 голосов
/ 24 августа 2011

Я читаю это бизон введение.

У меня есть два вопроса, и будет здорово, если кто-нибудь поможет мне понять:

  1. Что означает термин context free grammar?

  2. По ссылке выше: Bison может обрабатывать не все контекстно-свободные языки, только те, которые являются LALR (1). Вкратце, это означает, что должна быть возможность указать, как анализировать любую часть входной строки, используя только один токен предварительного просмотра. Что означает «можно сказать, как анализировать любую часть входной строки с помощью одного токена упреждения»?

Ответы [ 2 ]

21 голосов
/ 24 августа 2011

Контекстно-свободная грамматика - это описание набора строк, которое строго более мощное, чем регулярные выражения, но все же легко обрабатываемое машиной.Более формально, контекстно-свободная грамматика состоит из четырех вещей:

  • Набор терминальных символов , которые являются элементами производимых строк.Для синтаксического анализатора бизонов это обычно набор токенов, генерируемых сканером.Для грамматики для естественного языка, такого как английский, это может быть набор всех английских слов.

  • Набор нетерминальных символов .Интуитивно понятно, что нетерминальный символ представляет нечто вроде части речи, такой как «существительное» или «глагол».

  • Набор произведений. Каждое произведение говорит, какзаменить нетерминальный символ другим набором терминалов и нетерминалов.Например, производство Variable -> Type Name говорит, что если мы увидим нетерминал Variable, мы можем заменить его строкой Type Name.

  • A начальный символ, , который является нетерминалом, с которого начинается деривация.

В качестве примера рассмотрим эту простую контекстную грамматику для объявлений функций в стиле C:

Function -> Type ident(Arguments)
Type -> int
Type -> Type *
Arguments -> e   (the empty string)
Arguments -> ArgList
ArgList -> Type ident
ArgList -> Type ident, ArgList

Здесь начальный символ Function.Учитывая эту грамматику, мы можем создавать объявления функций в стиле C, многократно выбирая нетерминальный символ и заменяя его одной из правых частей соответствующего произведения.На каждом шаге строка, которую мы до сих пор строили, называется формой предложения .Например, вот несколько различных объявлений функций, которые могут быть получены из приведенной выше грамматики:

Sentential Form                       Production
-------------------------------------------------------------------
Function                              Function -> Type ident(Arguments)
Type ident(Arguments)                 Type -> int
int ident(Arguments)                  Arguments -> e
int ident()

Sentential Form                       Production
-------------------------------------------------------------------
Function                              Function -> Type ident(Arguments)
Type ident(Arguments)                 Type -> Type*
Type* ident(Arguments)                Type -> int
int* ident(Arguments)                 Arguments -> ArgList
int* ident(ArgList)                   ArgList -> Type ident, ArgList
int* ident(Type ident, ArgList)       ArgList -> Type ident
int* ident(Type ident, Type ident)    Type -> Type*
int* ident(Type* ident, Type ident)   Type -> Type*
int* ident(Type** ident, Type ident)  Type -> int
int* ident(int** ident, Type ident)   Type -> int
int* ident(int** ident, int ident)

Большинство языков программирования имеют структуру, которая может быть описана безконтекстной грамматикой.Работа синтаксического анализатора состоит в том, чтобы взять программу и грамматику и определить, как эта программа может быть сгенерирована грамматикой.

Что касается LALR (1), к сожалению, формальное определение LALR (1) не являетсятривиальный.Я только что закончил преподавать курс по компиляции, и мы смогли поговорить о LALR (1) только после того, как сначала провели две лекции, обсуждая связанные методы разбора.Если вы хотите получить официальное введение в материал, мои слайды по анализу снизу вверх доступны на сайте курса .

LALR (1) являетсятип алгоритма синтаксического анализа, называемый восходящим алгоритмом синтаксического анализа , что означает, что он пытается применить производство грамматики в обратном порядке, чтобы уменьшить программу обратно к начальному символу.Например, давайте рассмотрим эту строку, которая генерируется приведенной выше грамматикой:

int** ident(int* ident)

При анализе снизу вверх мы будем анализировать эту строку, просматривая программу по одному токену за раз.Всякий раз, когда мы обнаруживаем что-то, что может быть обращено обратно к какому-то нетерминалу, мы делаем это.(Чтобы быть более точным, LALR (1) делает подобные сокращения только тогда, когда встречаются другие критерии, так что алгоритм имеет больше контекста, но для этого примера нам не нужно беспокоиться об этом).Каждый шаг в разборе называется либо сдвиг , либо уменьшение . shift означает, что мы смотрим на еще один токен ввода, чтобы получить больше информации о том, какие сокращения применить.* уменьшение означает, что мы берем некоторое количество токенов и нетерминалов и обращаем производство, чтобы вернуться к какому-нибудь нетерминалу.

Вот след восходящего анализа строки:

Workspace           Input                     Action
-----------------------------------------------------------------
             int** ident(int* ident)   Shift
int             ** ident(int* ident)   Reduce Type -> int
Type            ** ident(int* ident)   Shift
Type*            * ident(int* ident)   Reduce Type -> Type*
Type             * ident(int* ident)   Shift
Type*              ident(int* ident)   Reduce Type -> Type*
Type               ident(int* ident)   Shift
Type ident              (int* ident)   Shift
Type ident(              int* ident)   Shift
Type ident(int              * ident)   Reduce Type -> int
Type ident(Type             * ident)   Shift
Type ident(Type*              ident)   Reduce Type -> Type*
Type ident(Type               ident)   Shift
Type ident(Type ident              )   Reduce ArgList -> Type ident
Type ident(ArgList                 )   Reduce Arguments -> ArgList
Type ident(Arguments               )   Shift
Type ident(Arguments)                  Reduce Function -> Type ident(Arguments)
Function                               ACCEPT

Важно знать о сдвигах и сокращениях, потому что вы будете постоянно встречать сдвигать / уменьшать конфликты и уменьшать / уменьшать конфликты при использовании зубров.Эти ошибки означают, что генератор синтаксического анализатора определил, что анализатор может войти в состояние, в котором он не может определить, сдвигать или уменьшать, или какое из двух сокращений он должен выполнить.Обратитесь к руководству по зубрам для более подробной информации о том, как справиться с этим.

Если вы хотите узнать больше о неконтекстных грамматиках и алгоритмах синтаксического анализа в целом, есть отличная книга под названием Методы синтаксического анализа: практическое руководство, второе издание от Грюна и Джейкобса, который, безусловно, является лучшей обработкой материала, который я когда-либо видел. Он охватывает все виды алгоритмов синтаксического анализа, включая многие методы, значительно более мощные, чем LALR (1), которые начинают шире использоваться (например, GLR и Earley). Я очень рекомендую эту книгу - это главная причина, по которой я так хорошо разбираюсь в разборе!

Надеюсь, это поможет!

1 голос
/ 24 августа 2011

1) Проще говоря - это означает, что форматирование и контекст кода не важен для отдельных частей, и вы не видите, как он отформатирован, чтобы понять смысл.Например, вы можете написать свою C-программу в одной строке или с каждым словом в другой строке, и программа будет означать то же самое. Википедия имеет более формальное определение.

2) Что означает LALR (1) более сложно, но в простых терминах я бы описал это как понимание значения постепенночитая одно слово за раз - видя только следующее слово / символ - некоторые разговорные языки известны тем, что они не являются LALR (1), - когда вы не понимаете предложение как вопрос или утверждение, когда дойдете до концапредложения.Некоторые языки программирования также могут быть сконструированы таким образом, но я не знаю ни одного, поскольку они по практическим причинам все соответствуют синтаксису LALR (1).Тем не менее, я думаю, что есть исключения, когда C / C ++ требуется 2 символа, чтобы правильно проанализировать операторы (следовательно, LALR (2), но, поскольку я не могу вспомнить, что это такое, надеюсь, кто-то укажет на этов комментарии.

В любом случае эта книга здесь является библией, когда дело доходит до понимания компиляторов и синтаксических анализаторов.

...