Как разделить обязанности между парсером и лексером для соответствия блоку текста? - PullRequest
0 голосов
/ 31 мая 2011

У меня есть текстовый файл, который я пытаюсь проанализировать. Файл выглядит так:

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 ':'

Однако следующий фрагмент текста вызывает некоторые проблемы. Я почти уверен, что мне нужно включить условия запуска в лексере (например, чтобы игнорировать только пробелы при разборе заголовка). Я пытался определить токен TEXT и проанализировать все это до ---------- как один токен. Все еще не могу понять, разумно ли это делать.

Другая возможность, о которой я могу подумать, - это иметь в грамматике правило, которое объединяет текстовый сегмент с использованием токенов, таких как WORD, SPACE, DASH, NEWLINE и любой другой возможный символ. Имеет ли это смысл?

Так что теперь я застрял, пытаясь разобрать эти сегменты текста. Я использую правильные инструменты для работы. Я буду признателен за вашу помощь, спасибо.

Ответы [ 2 ]

3 голосов
/ 01 июня 2011

Это то, для чего были разработаны стартовые состояния lex.По сути, вы объявляете начальное состояние для каждого языка, с которым вам нужно иметь дело (два в вашем случае - заголовки и тела), а затем помечаете правила в зависимости от того, к какому состоянию они применяются.Таким образом, вы хотите что-то вроде:

%s header
%s body
%%
<header>[ \t\n]                   ; /* 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 ;

Конечно, вы также захотите, чтобы код делал все, что вы хотите, с содержимым разделов тела ...

0 голосов
/ 31 мая 2011

Я пришел к выводу, что обработка такого документа построчно (то есть написание собственного синтаксического анализатора, подходящего для задачи) может привести к гораздо более чистому решению.

Другая возможность - разделить весь файл на каждый маркер __________. Таким образом, мы получим несколько разделов. Затем разделите каждую секцию по ---------- маркерам. Теперь мы можем извлечь второй фрагмент текста текущего раздела ( «Еще немного текста.» в первом разделе в примере выше). Первый фрагмент текста - это просто однострочный заголовок, за которым следует «Некоторый текст». до конца фрагмента.

Этот алгоритм легко реализуется на языке сценариев, таких как Perl, Python, Ruby и т. Д.

...