首先,不要为此使用REJECT。 REJECT 是一个可怕的 hack,它会极大地降低您的词法分析器,并且在解析器中几乎没有实际用例。
另外,它不会像你写的那样工作。在您拒绝与.* 的匹配后,返回的下一个匹配将是下一个较短的.* 匹配——即除最后一个字符之外的所有字符——而不是另一个模式的匹配(除非除最后一个字符之外的所有字符在该行中恰好匹配另一个具有更高优先级的模式)。
如果您想捕获并临时存储读取的每一行,最简单的方法是提供您自己的YY_INPUT,它一次读取一行输入。 (如果您的 C 库支持,我建议使用 Posix 2008 getline() 接口。)只需保存这一行(及其长度)即可。
现在,对于您的实际问题:在可重入词法分析器中将内容保存在何处,以便解析器可以看到它们。
幸运的是,flex 为a way for you to augment the yyscan_t context object 提供了您自己的任意“额外”数据yyextra。 (在扫描仪代码本身之外,您需要使用访问器yyget_extra(scanner) 来获取此值。)默认情况下,yyextra 的类型为void *,但您可以使用%option extra-type 覆盖它。
yyextra 通常是指向您自己的上下文对象的指针,尽管如果您不需要太多上下文,也可以将 yyextra 设为一个小结构。 (yyget_extra 和 yyset_extra 按值传递 yyextra,这不适用于许多上下文对象。)
这里有一个(有点)简单的例子。下面的大部分样板都取自this answer 中的骨架;请参阅该链接以了解大多数奇怪之处。
文件示例.l
%option noyywrap 8bit noinput nounput nodefault
%option reentrant bison-bridge bison-locations yylineno
%option extra-type="struct ScanExtra*"
%{
#include "sample.tab.h"
#define YY_INPUT(buf,result,max_size) do { \
yyextra->linebuf_len = getline(&yyextra->linebuf, \
&yyextra->linebuf_size, yyin); \
if (yyextra->linebuf_len == -1) result = YY_NULL; \
else if (yyextra->linebuf_len <= max_size) { \
memcpy(buf, yyextra->linebuf, yyextra->linebuf_len); \
result = yyextra->linebuf_len; \
} \
else { /* Handle long lines */ } \
} while(0)
%}
%%
[[:space:]] ; /* Ignore whitespace */
#.* ; /* Ignore comments */
[[:alnum:]]+ { yylval->str = strdup(yytext); return WORD; }
. { return *yytext; }
%%
/* These functions are here for simplicity. Normally, I'd put them in a
* separate parse_utils.c file.
* They're declared in sample.h (except for yyerror).
*/
/* Creates a scanner with an initialised ScanExtra */
yyscan_t myscanner_create(void) {
struct ScanExtra* extra = calloc(1, sizeof *extra);
yyscan_t scanner;
yylex_init_extra(extra, &scanner);
return scanner;
}
/* Destroys a scanner */
void myscanner_destroy(yyscan_t scanner) {
free(yyget_extra(scanner));
yylex_destroy(scanner);
}
#include <ctype.h>
void yyerror(YYLTYPE* yyllocp, yyscan_t scanner, const char* msg) {
/* Get rid of trailing whitespace in the current line */
const char* buf = yyget_extra(scanner)->linebuf;
int len = yyget_extra(scanner)->linebuf_len;
while (len && isspace(buf[len - 1])) --len;
fprintf(stderr, "Syntax error near '%s' in line %d: '%.*s'\n",
yyget_text(scanner),
yyget_lineno(scanner),
len, buf);
}
文件 sample.y
%define api.pure full
%locations
%param { yyscan_t scanner }
%code top {
#include <stdio.h>
}
%code requires {
typedef void* yyscan_t;
/* I define ScanExtra here so that it goes into the generated header file */
struct ScanExtra {
char* linebuf;
size_t linebuf_size;
ssize_t linebuf_len;
};
}
%code {
int yylex(YYSTYPE* yylvalp, YYLTYPE* yyllocp, yyscan_t scanner);
void yyerror(YYLTYPE* yyllocp, yyscan_t scanner, const char* msg);
}
%union {
char* str;
}
%token <str> WORD
%%
item: WORD { free($1); }
| '(' opt_item_list ')'
item_list: item | item_list ',' item
opt_item_list: %empty | item_list
文件样本.h
#include "sample.tab.h"
#include "sample.lex.h"
yyscan_t myscanner_create(void);
void myscanner_destroy(yyscan_t);
文件:生成文件
all: sample
sample.lex.c: sample.l
flex -o $@ --header-file=$(patsubst %.c,%.h,$@) --debug $<
sample.tab.c: sample.y
bison -o $@ --defines=$(patsubst %.c,%.h,$@) --debug $<
sample: main.c sample.tab.c sample.lex.c sample.h
$(CC) -o $@ -Wall --std=c11 -ggdb -D_XOPEN_SOURCE=700 $(filter %.c,$^)
clean:
rm -f sample.tab.c sample.lex.c sample.tab.h sample.lex.h sample