#bison #yacc #lex #flex-lexer
#bison #yacc #lex #flex-lexer
Вопрос:
У меня есть текстовый файл, который я пытаюсь разобрать. Файл выглядит следующим образом:
A_1: - A_10:
Some text.
----------
Some more text.
__________
B_1: - B_5:
Still more text - it may contain dashes as well.
----------
Even more text. Could be multiple sentences with newlines.
Like this.
__________
И так далее.
Я пытаюсь разделить синтаксический анализ / токенизацию между bison
и flex
. Мне удалось разобрать заголовок ( A_1: - A_10:
), используя следующие регулярные выражения в flex
:
[ t] ; // ignore whitespace
[A-Z]_[0-9] (_[0-9] )? { ... return ID; }
в сочетании с правилом в моей грамматике для объединения двух идентификаторов:
header: ID ':' '-' ID ':'
Однако следующий сегмент текста вызывает некоторые проблемы. Я почти уверен, что мне нужно включить начальные условия в лексер (например, игнорировать пробелы только при разборе заголовка). Я попытался определить ТЕКСТ токена и разобрать все это до ----------
единого токена. Все еще не могу понять, разумно ли это делать.
Другая возможность, о которой я могу подумать, — это иметь правило в грамматике, которое объединяло бы текстовый сегмент с использованием таких символов, как WORD, ПРОБЕЛ, ТИРЕ, ПЕРЕВОД строки и любой другой возможный символ. Имеет ли это вообще смысл?
Итак, теперь я застрял, пытаясь разобрать эти текстовые сегменты. Использую ли я правильные инструменты для работы. Я буду признателен за вашу помощь, спасибо.
Ответ №1:
Это то, для чего были разработаны начальные состояния lex. По сути, вы объявляете начальное состояние для каждого отдельного языка, с которым вам нужно иметь дело (в вашем случае два — заголовки и тела), а затем помечаете правила тегами, основанными на том, к какому состоянию они применяются. Итак, вы хотели бы что-то вроде:
%s header
%s body
%%
<header>[ tn] ; /* ignore */
<header>[_a-zA-Z][_a-zA-Z0-9]* { ... return ID; }
<header>[-:,.;()] { return *yytext; }
<body>^----------$ { yylval.text = GetStoredText(); return SECTION_SPLIT; }
<body>^__________$ { yylval.text = GetStoredText(); return SECTION_END; }
<body>. { StoreText(*yytext);
%%
void BeginHeader() { BEGIN header; }
void BeginBody() { BEGIN body; }
Где StoreText
это функция, которая сохраняет символы в буфер (что-то вроде std::stringstream, если вы используете C ) и GetStoredText
возвращает весь текст, сохраненный с момента последнего вызова, и очищает буфер. Тогда ваш код yacc / bison будет выглядеть примерно так:
input: entry | input entry ;
entry: header body ;
header: ..something to match a header.. { BeginBody(); };
body: sections SECTION_END { BeginHeader(); };
sections: /*empty*/ | sections SECTION_SPLIT ;
Конечно, вы также захотите, чтобы код делал все, что вы хотите, с содержимым основных разделов…
Комментарии:
1. Спасибо! Это отлично работает. Единственное исправление, которое я внес, это заменить последний шаблон на этот
<body>(?s:.)
, чтобы соответствовать новым строкам и всему остальному между разделителями разделов.
Ответ №2:
Я пришел к осознанию того, что обработка такого документа построчно (т. Е. написание собственного синтаксического анализатора, подходящего для задачи) может привести к гораздо более чистому решению.
Другая возможность — разделить весь файл по каждому __________
маркеру. Таким образом, мы получим несколько разделов. Затем разделите каждый раздел на ----------
маркеры. Теперь мы можем извлечь второй фрагмент текста текущего раздела («Еще немного текста». в первом разделе в примере выше). Первый фрагмент текста — это просто однострочный заголовок, за которым следует «Некоторый текст». до конца фрагмента.
Этот алгоритм легко реализуется на языках сценариев, таких как Perl, Python, Ruby и т.д.
Комментарии:
1. Я сомневаюсь в этом, но, возможно, вам следует попробовать использовать что-то вроде json или yaml для вашего ввода. Тогда вам вообще не нужно было бы внедрять синтаксический анализатор. yaml и json довольно хороши.
2. @acidzombie24 Для своих собственных нужд я всегда использую структурированный формат для хранения данных. Однако на этот раз вопрос касается синтаксического анализа файла в заданном формате, который я не могу изменить самостоятельно. Спасибо за предложение
![]()
3. черт возьми, я ненавижу эти ограничения. Хорошо, но я думаю, что ответом Доддса легче управлять, чем писать свой собственный синтаксический анализатор. Вы в конечном итоге написали свой собственный? было ли это сложно и сколько времени это заняло?
4. @acidzombie24 Я написал свой собственный на Python, прежде чем Крис опубликовал свой ответ. Это было довольно быстро и просто, и было очень легко сохранить результат в json / plist. На C я бы потратил еще немного времени на выяснение этого.
5. ах, действительно? мне любопытно, как выглядит код. Тогда это звучит нормально. Я определенно не рекомендовал бы это, если бы это было на c (наконец, C получает regex как часть стандарта)