作为使用yacc(或bison)和lex(或flex)的典型解析器的一部分,我想复制词法分析器中的整个输入行,这样,如果以后出现错误,程序可以打印出违规行为整个行,并在违规令牌下放置一个插入符号^
。
要复制该行,我现在正在做:
char *line; // holds copy of entire line
bool copied_line;
%%
^.+ {
if ( !copied_line ) {
free( line );
line = strdup( yytext );
copied_line = true;
}
REJECT;
}
/* ... other tokens ... */
\n { copied_line = false; return END; }
这很有效,但是,从踩入调试器开始,它的效率非常低。似乎正在发生的是REJECT
导致词法分析者一次退出一个角色而不是跳到下一个可能的匹配。
是否有更好,更有效的方式来获得我想要的东西?
答案 0 :(得分:1)
Based on the hint from @Serge Ballesta of using YY_INPUT
:
#define YY_INPUT( BUF, RESULT, MAX_SIZE ) \
(RESULT) = lexer_get_input( (BUF), (MAX_SIZE) )
static size_t column; // current 0-based column
static char *input_line;
static size_t lexer_get_input( char *buf, size_t buf_size ) {
size_t bytes_read = 0;
for ( ; bytes_read < buf_size; ++bytes_read ) {
int const c = getc( yyin );
if ( c == EOF ) {
if ( ferror( yyin ) )
/* complain and exit */;
break;
}
buf[ bytes_read ] = (char)c;
if ( c == '\n' )
break;
} // for
if ( column == 0 && bytes_read < buf_size ) {
static size_t input_line_capacity;
if ( input_line_capacity < bytes_read + 1/*null*/ ) {
input_line_capacity = bytes_read + 1/*null*/;
input_line = (char*)realloc( input_line, input_line_capacity );
}
strncpy( input_line, buf, bytes_read );
input_line_len = bytes_read;
input_line[ input_line_len ] = '\0';
}
return bytes_read;
}
The first time this is called, column
will be 0, so it will copy the entire line into input_line
. On subsequent calls, nothing special needs to be done. Eventually, column
will be reset to 0 upon encountering a newline; then the next time the function is called, it will again copy the line.
This seems to work and is a lot more efficient. Anybody see any problems with it?
答案 1 :(得分:1)
以下是YY_INPUT
使用getline()
的可能定义。只要没有令牌包含换行符和后续字符,它就应该工作。 (令牌最后可能包含换行符。)具体来说,current_line
将包含当前令牌的最后一行。
成功完成词法扫描后,current_line
将被释放,剩余的全局变量将被重置,以便可以对词法分析另一个输入。如果在到达输入结束之前停止了词法扫描(例如,因为解析不成功),则应对reset_current_line()
进行显式调用以执行这些任务。
char* current_line = NULL;
size_t current_line_alloc = 0;
ssize_t current_line_sent = 0;
ssize_t current_line_len = 0;
void reset_current_line() {
free(current_line);
current_line = NULL;
current_line_alloc = current_line_sent = current_line_len = 0;
}
ssize_t refill_flex_buffer(char* buf, size_t max_size) {
ssize_t avail = current_line_len - current_line_sent;
if (!avail) {
current_line_sent = 0;
avail = getline(¤t_line, ¤t_line_alloc, stdin);
if (avail < 0) {
if (ferror(stdin)) { perror("Could not read input: "); }
avail = 0;
}
current_line_len = avail;
}
if (avail > max_size) avail = max_size;
memcpy(buf, current_line + current_line_sent, avail);
current_line_sent += avail;
if (!avail) reset_current_line();
return avail;
}
#define YY_INPUT(buf, result, max_size) \
result = refill_flex_buffer(buf, max_size);
虽然上面的代码不依赖于维护当前列的位置,但是如果要识别当前行在当前行中的位置,则很重要。如果您不使用yyless
或yymore
:
size_t current_col = 0, current_col_end = 0;
/* Call this in any token whose last character is \n,
* but only after making use of column information.
*/
void reset_current_col() {
current_col = current_col_end = 0;
}
#define YY_USER_ACTION \
{ current_col = current_col_end; current_col_end += yyleng; }
如果您将此扫描仪与具有前瞻的解析器一起使用,则仅保留输入流的一行可能是不够的,因为前瞻令牌可能位于错误令牌的后续行上。将几个保留的行保留在循环缓冲区中将是一个简单的增强,但显然不需要多少行。
答案 2 :(得分:0)
假设您的输入源于可搜索流:
即使输入来自不可搜索的流,您也可以将所有字符保存在临时存储中。
此主题的变体是可能的,例如存储上次看到的换行符的偏移量,因此您可以直接寻找它。
答案 3 :(得分:0)
在flex中,您可以使用YY_USER_ACTION
,如果定义为宏,则会在运行令牌操作之前为每个令牌运行。如下所示:
#define YY_USER_ACTION append_to_buffer(yytext);
会将yytext
附加到缓冲区,稍后您可以使用它。