Как я могу передать открытый файл, например читать другим сканером в следующий сканер - и отдавать парсеру?
flex / bison: как переключить два лексера в одном входном файле
Ответы (1)
Буферы Flex нельзя легко перенести с одного сканера на другой. Многие детали являются конфиденциальными для сканера, и их потребуется реконструировать, что приведет к потере ремонтопригодности.
Однако нетрудно объединить два (или более) определения сканера в один сканер при условии, что семантические типы совместимы. Просто нужно дать им разные стартовые условия. Поскольку условие запуска может быть установлено даже вне действия сканера, легко переключиться с одного определения сканера на другое.
Поскольку сканеры Flex основаны на таблицах, объединение двух сканеров не является неэффективным; действительно, может быть некоторая ценность в том, чтобы не дублировать код. Объединенная таблица может быть немного больше, чем сумма отдельных таблиц, потому что, вероятно, будет больше классов эквивалентности символов, но, с другой стороны, большая таблица может обеспечить лучшее сжатие таблицы. Ни один из этих эффектов вряд ли будет заметен.
Вот простой, но, возможно, полезный пример. Этот синтаксический анализатор читает файл и заменяет ${arithmetic expressions}
вычисленным выражением. (Поскольку это всего лишь пример, разрешены только очень простые выражения, но их должно быть легко расширить.)
Поскольку лексический сканер должен запускаться в состоянии запуска SC_ECHO
, его необходимо инициализировать. Лично я бы предпочел начать с INITIAL
, чтобы избежать этой инициализации в этом простом случае, но иногда сканерам необходимо уметь обрабатывать различные условия запуска, поэтому я оставил код. Обработку ошибок можно улучшить, но она работает.
Синтаксический анализатор использует очень простое error
правило для повторной синхронизации и отслеживания ошибок подстановки. Семантическое значение нетерминалов subst
, file
и start
- это счетчик ошибок для файла; семантическое значение для expr
- это значение выражения. В этом простом случае они оба являются просто целыми числами, поэтому работает тип по умолчанию для yylval
.
Незавершенные замены не обрабатываются изящно; в частности, если EOF читается во время лексического сканирования для замены, в вывод не вставляется никакая индикация. Я оставляю это как упражнение. :)
Вот лексер:
%{
#include "xsub.tab.h"
%}
%option noinput nounput noyywrap nodefault
%option yylineno
%x SC_ECHO
%%
/* In a reentrant lexer, this would go into the state object */
static int braces;
/* This start condition just echos until it finds ${... */
<SC_ECHO>{
"${" braces = 0; BEGIN(INITIAL);
[^$\n]+ ECHO;
"$" ECHO;
\n ECHO;
}
/* We need to figure out where the substitution ends, which is why we can't
* just use a standard calculator. Here we deal with terminations.
*/
"{" ++braces; return '{';
"}" { if (braces) { --braces; return '}'; }
else { BEGIN(SC_ECHO); return FIN; }
}
/* The rest is just a normal calculator */
[0-9]+ yylval = strtol(yytext, NULL, 10); return NUMBER;
[[:blank:]]+ /* Ignore white space */
\n /* Ignore newlines, too (but could also be an error) */
. return yytext[0];
%%
void initialize_scanner(void) {
BEGIN(SC_ECHO);
}
Парсер экспортирует единый интерфейс:
int parseFile(FILE *in, *out);
который возвращает 0, если все прошло хорошо, и в противном случае количество неправильных замен (по модулю упомянутой выше проблемы с незавершенными заменами). Вот файл:
%{
#include <stdio.h>
int yylex(void);
void yyerror(const char* msg);
void initialize_scanner(void);
extern int yylineno;
extern FILE *yyin, *yyout;
%}
%token NUMBER FIN UNOP
%left '+' '-'
%left '*' '/' '%'
%nonassoc UNOP
%define parse.lac full
%define parse.error verbose
%%
start: file { if ($1) YYABORT; else YYACCEPT; }
file : { $$ = 0; }
| file subst { $$ = $1 + $2; }
subst: expr FIN { fprintf(yyout, "%d", $1); $$ = 0; }
| error FIN { fputs("${ BAD SUBSTITUTION }", yyout); $$ = 1; }
expr : NUMBER
| '-' expr %prec UNOP { $$ = -$2; }
| '(' expr ')' { $$ = $2; }
| expr '+' expr { $$ = $1 + $3; }
| expr '-' expr { $$ = $1 - $3; }
| expr '*' expr { $$ = $1 * $3; }
| expr '/' expr { $$ = $1 / $3; }
| expr '%' expr { $$ = $1 % $3; }
%%
void yyerror(const char* msg) {
fprintf(stderr, "%d: %s\n", yylineno, msg);
}
int parseFile(FILE* in, FILE* out) {
initialize_scanner();
yyin = in;
yyout = out;
return yyparse();
}
И простой драйвер:
#include <stdio.h>
int parseFile(FILE* in, FILE* out);
int main() {
return parseFile(stdin, stdout);
}
BEGIN (ignore)
, или использовать другое правило lex для установки начального условия. Но я не должен инициализировать начальное условие поверх всех определений. Спасибо всем, кто помог мне разобраться в механизме. (Здесь я также не знал, что могу вызвать BEGIN
в кодировке)
- person sqller; 28.08.2016
++braces
. Я использовал несколько {
, но в итоге получаю ошибки.
- person user1424739; 15.02.2019