提供输入流的机制flex
是提供一个定义YY_INPUT http://westes.github.io/flex/manual/Generated-Scanner.html#index-YY_005fINPUT_002c-overriding宏,每次都会调用flex
需要重新填充其缓冲区[注1]。该宏使用三个参数调用,大致如下:
YY_INPUT(buffer, &bytes_read, max_bytes)
该宏预计读取至max_bytes
into buffer
,并设置bytes_read
到实际读取的字节数。如果该流中不再有输入,YY_INPUT
应该设置bytes_read
to YY_NULL
(即 0)。除了设置文件结束条件之外,没有其他方法可以标记输入错误。不设置YY_INPUT
为负值。
注意YY_INPUT
不提供从哪里读取输入或任何类型的指示userdata
争论。唯一提供的机制是全局的yyin
,这是一个FILE*
。 (您可以创建一个FILE*
来自文件/套接字描述符fdopen
并获取描述符fileno
。其他解决方法超出了本答案的范围。)
当扫描器遇到流的末尾时,如YY_INPUT
返回0,它完成当前令牌[注2],然后调用yywrap
决定是否还有另一个流要处理。正如手册所示,它不会重置解析器状态(即,它恰好处于哪个启动条件;如果启用了行计数,则为当前行号等)。然而,它不允许令牌跨越两个流。
The yywrap
当解析器/扫描器应用于命令行上指定的多个不同文件时,最常使用该机制。在该用例中,如果令牌可以在一个文件中开始并继续进入另一个文件,那就有点奇怪了;大多数语言实现都希望它们的文件在某种程度上是独立的。 (例如,考虑多行字符串文字。)通常,您实际上还想重置更多解析器状态(当然是行号,有时是开始条件),但这是yywrap
。 [注3]
对于从套接字进行词法分析,您可能需要调用recv
从你的YY_INPUT
执行。但出于实验目的,这里有一个简单的YY_INPUT
它只从内存缓冲区返回数据:
/* Globals which describe the input buffer. */
const char* my_in_buffer = NULL;
const char* my_in_pointer = NULL;
const char* my_in_limit = NULL;
void my_set_buffer(const char* buffer, size_t buflen) {
my_in_buffer = my_in_pointer = buffer;
my_in_limit = my_in_buffer + buflen;
}
/* For debugging, limit the number of bytes YY_INPUT will
* return.
*/
#define MY_MAXREAD 26
/* This is technically incorrect because it returns 0
* on EOF, assuming that YY_NULL is 0.
*/
#define YY_INPUT(buf, ret, maxlen) do { \
size_t avail = my_in_limit - my_in_pointer; \
size_t toread = maxlen; \
if (toread > avail) toread = avail; \
if (toread > MY_MAXREAD) toread = MY_MAXREAD; \
*ret = toread; \
memcpy(buf, my_inpointer, toread); \
my_in_pointer += toread; \
} while (0)
Notes
这并不完全正确。缓冲器状态包括指示缓冲器是否可以被重新填充的标志。如果你使用yy_scan_bytes
,创建的缓冲区状态被标记为不可重新填充。
实际上比这更复杂一些,因为 Flex 扫描器有时需要向前看,以便决定哪个令牌已匹配,并且在向前看期间可能会出现流结束指示。在扫描器备份到已识别令牌的末尾后,它仍然必须重新扫描先行字符,其中可能包含更多令牌。为了处理这个问题,它在缓冲区状态中设置一个标志,指示已到达流末尾,从而防止YY_INPUT
每次扫描器到达缓冲区末尾时都不会被调用。尽管如此,确保您的YY_INPUT
如果在流结束返回后再次调用它,实现将继续返回流结束。
另一个具体的例子,假设你想实现某种#include
机制。flex
提供了yy_push_state/yy_pop_state
允许您实现包含堆栈的机制。你会打电话yy_push_state
一旦include
指令已被扫描,但是yy_pop_state
需要从以下位置调用yywrap
。同样,很少有语言允许令牌在包含的源文件中启动并继续遵循include
指示。