Синтаксический анализатор Конечного автомата

Я хотел бы проанализировать саморазработанный формат файла с подобным FSM синтаксическим анализатором в C++ (это - a teach-myself-c++-the-hard-way-by-doing-something-big-and-difficult вид проекта :)). У меня есть маркируемая строка с новыми строками, показывающими конец euh... строка. Посмотрите здесь для входного примера. Все комментарии будут и спам, который будет отфильтровываться, таким образом, у меня будет станд.:: представьте в виде строки как это:

global \n { \n SOURCE_DIRS src \n HEADER_DIRS include \n SOURCES bitwise.c framing.c \n HEADERS ogg/os_types.h ogg/ogg.h \n } \n ...

Объяснение синтаксиса:

  • {} объемы, и использованные для своей выгоды слова показывают, что список опций/файлов должен следовать.
  • \n только важны в списке опций/файлов, показывая конец списка.

Таким образом, я думал, что FSM будет достаточно прост/расширяем для моих потребностей/знания. Насколько я могу сказать (и хотеть, чтобы мой дизайн файла был), мне не нужны параллельные состояния, или что-либо полагает как этот. Некоторые вопросы о дизайне/реализации:

  1. Если я использую enum или краткий обзор class + производные для моих состояний? Первое, вероятно, лучше для маленького синтаксиса, но могло стать ужасным позже, и второй является полная противоположность. Я склоняюсь к первому для его простоты. enum пример и пример класса.Править: что относительно этого предложения для goto, Я думал, что они были злыми в C++?
  2. При чтении списка я не должен игнорировать \n. Мой предпочтительный способ использовать string через stringstream, проигнорирует \n по умолчанию. Таким образом, мне нужен простой способ сказать (то же!) stringstream не проигнорировать новые строки, когда определенное состояние включено.
  3. Будет простое enum состояния достаточны для многоуровневого парсинга (объемы в объемах {...{...}...}) или этому были бы нужны hacky реализации?
  4. Вот черновые состояния, которые я имею в виду:
    • upper: глобальные чтения, exe, lib + предназначаются для имен...
    • normal: в объеме, может считать ИСТОЧНИКИ..., создать пользовательские переменные...
    • list: добавляют объекты к списку, пока с новой строкой не встречаются.

Каждый объем будет иметь своего рода условное выражение (например, win32:global {gcc:CFLAGS =...}), и должен будет быть обработан тем же самым способом eveywhere (даже в list состояние, на объект).

Спасибо за любой вход.

5
задан Community 23 May 2017 в 12:07
поделиться

3 ответа

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

Если вы добавляете стек к FSM, то попадаете на территорию pushdown automaton. Недетерминированный pushdown-автомат эквивалентен контекстно-свободной грамматике (хотя детерминированный pushdown-автомат строго менее мощный). Генераторы синтаксического анализатора LALR(1) фактически генерируют детерминированный автомат pushdown внутри себя. В хорошем учебнике по проектированию компиляторов будет описан точный алгоритм, с помощью которого автомат pushdown строится из грамматики. (Таким образом, добавление стека не является "халтурой"). Эта статья Википедии также описывает, как построить автомат LR(1) pushdown из вашей грамматики, но, IMO, статья не настолько ясна, как могла бы быть.

Если ваши диапазоны вложены только в конечную глубину (т.е. у вас есть уровни upper, normal и list, но нет вложенных lists или вложенных normals), то вы можете использовать FSM без стека.

14
ответ дан 18 December 2019 в 09:05
поделиться

Для синтаксического анализа я всегда стараюсь использовать то, что уже доказано, что работает: ANTLR с ANTLRWorks , что очень помогает при разработке и тестировании грамматики. Вы можете сгенерировать код для C / C ++ (и других языков ), но вам необходимо создать среду выполнения ANTLR для этих языков.

Конечно, если вы найдете flex или bison более простыми в использовании, вы тоже можете их использовать (я знаю, что они генерируют только C и C ++, но я могу ошибаться, так как я этого не делал) не пользуюсь ими какое-то время).

1
ответ дан 18 December 2019 в 09:05
поделиться

Есть два этапа анализа входного потока текста для синтаксического анализа:

Лексический анализ: Здесь ваш входной поток разбивается на лексические единицы. Он просматривает последовательность символов и генерирует токены (аналогично слову в устных или письменных языках). Конечные автоматы очень хороши в лексическом анализе при условии, что вы приняли правильное проектное решение о лексической структуре. Исходя из приведенных выше данных, отдельными лексемами могут быть такие вещи, как ключевые слова (например, «глобальный»), идентификаторы (например, «побитовый», «ИСТОЧНИКИ»), символические токены (например, «{« »}», «.», «/» ), числовые значения, escape-значения (например, "\ n") и т. д.

Синтаксический / грамматический анализ: После создания последовательности токенов (или, возможно, пока вы это делаете) вам необходимо иметь возможность проанализируйте структуру, чтобы определить, соответствует ли последовательность токенов вашему языковому дизайну. Обычно для этого нужен какой-то парсер, хотя, если структура языка не очень сложна, вы можете сделать это с помощью конечного автомата. В общем (и поскольку вам нужны структуры вложенности в конкретном случае) вам нужно будет использовать один из методов, описанных Кеном Блумом.

Итак, в ответ на ваши вопросы:

Должен ли я использовать перечисление или абстрактный класс + производные для моих состояний?

Я обнаружил, что для небольших токенизаторов подходит матрица значений состояния / перехода, что-то вроде next_state = state_transitions [current_state] [current_input_char] .В этом случае next_state и current_state являются некоторыми целочисленными типами (включая, возможно, перечислимый тип). Ошибки ввода обнаруживаются при переходе в недопустимое состояние. Конец токена идентифицируется на основе идентификации состояния допустимых конечных состояний без допустимого перехода в другое состояние с учетом следующего входного символа. Если вас беспокоит космос, вы можете вместо этого использовать вектор карт. Создание классов состояний возможно, но я думаю, что это, вероятно, усложняет задачу, чем вам нужно.

При чтении списка НЕ ​​нужно игнорировать \ n.

Вы можете создать токен с именем "\ n" или более общий escape-токен (идентификатор, которому предшествует обратная косая черта. Если вы говорите об идентификации разрывов строк в исходном тексте, то это просто символы, которые вам нужно создавать переходы для в вашей матрице переходов состояний (однако следует помнить о различиях между разрывами строк в Unix и Windows; вы можете создать автомат, который будет работать с любым из них).

Достаточно ли простых состояний перечисления для многоуровневого синтаксического анализа (области видимости в пределах границ {... {...} ...}) или для этого потребуются хакерские реализации?

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

Вот черновые положения, которые я имею в виду: ...

См. Мои комментарии по лексическому и грамматическому анализу выше.

3
ответ дан 18 December 2019 в 09:05
поделиться
Другие вопросы по тегам:

Похожие вопросы: