在 C++ Flex 中更改 yylex
Change yylex in C++ Flex
我想将 yylex
更改为 alpha_yylex
,它也接受一个向量作为参数。
.
.
#define YY_DECL int yyFlexLexer::alpha_yylex(std::vector<alpha_token_t> tokens)
%}
.
.
. in main()
std::vector<alpha_token_t> tokens;
while(lexer->alpha_yylex(tokens) != 0) ;
我想我知道为什么会失败,因为显然在 FlexLexer.h
中没有 alpha_yylex
,但我不知道如何实现我想要的...
如何制作自己的 alpha_yylex() 或修改现有的?
您确实无法编辑 yyFlexLexer
的定义,因为 FlexLexer.h
实际上是一个 system-wide header 文件。但您当然可以将其子class,它将提供您所需要的大部分内容。
Subclassing yyFlexLexer
Flex 允许您使用 %option yyclass
(或 --yyclass
command-line 选项)来指定子 class 的名称,它将代替 yyFlexLexer
定义yylex
。 Subclassing yyFlexLexer
允许您包含自己的 header,它定义了您的 subclass' 成员,甚至可能还有其他函数,以及它的构造函数;简而言之,如果您的意图只是用连续的标记填充 std::vector<alpha_token_t>
,您可以通过将 AlphaLexer
定义为 yyFlexLexer
的子 class 来轻松地做到这一点,其中一个名为 tokens
的实例成员(或者,也许,具有访问函数)。
您还可以向新的 class 添加额外的成员函数,这可能会提供您需要这些额外参数的功能。
虽然在 C 接口中使用 YY_DECL
宏可以很容易地完成,但不完全是 straight-forward 的事情是更改由生成的扫描函数的名称和原型柔性。可以做到(见下文),但不清楚它是否真正受支持。无论如何,对于 C++,它可能不太重要。
除了由 Flex 的 C++ classes [注 1] 的奇怪组织造成的小问题外,子 class 词法分析器 class 很简单。您需要从 yyFlexLexer
[注 2] 中导出 class,它在 FlexLexer.h
中声明,并且您需要告诉 Flex 您的 class 的名称是什么,要么通过在 Flex 文件中使用 %option yyclass
,或在命令行中使用 --yyclass
.
指定名称
yyFlexLexer
包括用于操作输入缓冲区的各种方法,以及标准框架使用的词法扫描器的所有可变状态。 (其中大部分实际上是从基础 class FlexLexer
派生的。)它还包括一个带有原型
的虚拟 yylex
方法
virtual int yylex();
当您使用 subclass yyFlexLexer
时,yyFlexLexer::yylex()
被定义为通过调用 yyFlexLexer::LexerError(const char*)
发出错误信号并且生成的扫描器被定义为 class 定义为 yyclass
。 (如果不subclass,生成的scanner是yyFlexLexer::yylex()
。)
一个问题是您需要声明子class 的方式。通常,您会在 header 文件中这样做:
文件:myscanner.h(不要使用这个版本)
#pragma once
// DON'T DO THIS; IT WON'T WORK (flex 2.6)
#include <yyFlexLexer.h>
class MyScanner : public yyFlexLexer {
// whatever
};
然后您将 #include "myscanner.h"
在任何需要使用扫描仪的文件中,包括生成的扫描仪本身。
不幸的是,这不会起作用,因为它会导致 FlexLexer.h
在生成的扫描器中被包含两次; FlexLexer.h
没有通常意义上的包含保护,因为它被设计为被多次包含以支持 prefix
选项。所以需要定义两个header个文件:
文件:myscanner-internal.h
#pragma once
// This file depends on FlexLexer.h having already been included
// in the translation unit. Don't use it other than in the scanner
// definition.
class MyScanner : public yyFlexLexer {
// whatever
};
文件:myscanner.h
#pragma once
#include <FlexLexer.h>
#include "myscanner.h"
然后在每个需要了解扫描仪的文件中使用 #include "myscanner.h"
除了 扫描仪定义本身。在您的 myscanner.ll
文件中,您将 #include "myscanner-internal.h"
,这是有效的,因为 Flex 在插入扫描仪定义中的序言 C++ 代码之前已经包含 FlexLexer.h
。
更改 yylex 原型
你真的不能改变yylex
的原型(或名称),因为它是在FlexLexer.h
中声明的,而且如上所述,定义为发出错误信号。但是,您可以重新定义 YY_DECL
以创建 new 扫描仪界面。为此,您必须首先 #undef
现有的 YY_DECL
定义,至少在您的扫描仪定义中,因为带有 %option yyclass="MyScanner" contains
#define YY_DECL int MyScanner::yylex 的扫描仪(). That would make your
myscanner-internal.h` 文件看起来像这样:
#pragma once
// This file depends on FlexLexer.h having already been included
// in the translation unit. Don't use it other than in the scanner
// definition.
#undef YY_DECL
#define YY_DECL int MyScanner::alpha_yylex(std::vector<alpha_token_t>& tokens)
#include <vector>
#include "alpha_token.h"
class MyScanner : public yyFlexLexer {
public:
int alpha_yylex(std::vector<alpha_token_t>& tokens);
// whatever else you need
};
事实上 MyScanner
object 仍然有一个(不是很实用)yylex
方法可能不是问题。 FlexLexer
中有一些未记录的接口调用 yylex()
,但如果您不使用它们,这些都没有关系。 (无论如何,它们并不是那么有用。)但是您至少应该知道该接口存在。
无论如何,我不认为重命名 yylex
有什么意义(但也许您的审美不同)。它已经通过成为特定 class(MyScanner
,上文)的成员而有效地命名空间,因此 yylex
不会真正造成任何混淆。
在 std::vector<alpha_token_t>&
参数的特殊情况下,在我看来更简洁的解决方案是将引用作为成员变量放在 MyScanner
class 中并设置它与构造函数或访问器方法。除非您实际上在词法分析的不同点使用不同的向量——在您的问题的示例代码中并不明显——没有必要让每个调用站点都需要将向量的地址传递到 yylex
称呼。由于词法分析器操作是在 yylex
内部编译的,它是 MyScanner
的成员函数,实例变量——甚至是私有实例变量——在词法分析器操作中是可用的秒。当然,这不是额外 yylex
参数的唯一用例,但这是一个很常见的用例。
备注
根据生成代码中的注释,“C++ 接口一团糟”。
使用%option prefix
,您可以根据需要将yy
更改为其他内容。这个功能据说是为了让你在同一个项目中包含多个词法扫描器。但是,如果您计划使用 subclassing,所有这些词法扫描器的基础 classes 将是相同的(除了它们的名称)。因此,具有不同的基 classes 几乎没有意义。使用 %option prefix
重命名扫描仪 class 不如 subclassing 灵活且效率不高,并且它会产生额外的 header 并发症。 (有关详细信息,请参阅 this older answer。)所以我建议坚持使用 subclassing.
我想将 yylex
更改为 alpha_yylex
,它也接受一个向量作为参数。
.
.
#define YY_DECL int yyFlexLexer::alpha_yylex(std::vector<alpha_token_t> tokens)
%}
.
.
. in main()
std::vector<alpha_token_t> tokens;
while(lexer->alpha_yylex(tokens) != 0) ;
我想我知道为什么会失败,因为显然在 FlexLexer.h
中没有 alpha_yylex
,但我不知道如何实现我想要的...
如何制作自己的 alpha_yylex() 或修改现有的?
您确实无法编辑 yyFlexLexer
的定义,因为 FlexLexer.h
实际上是一个 system-wide header 文件。但您当然可以将其子class,它将提供您所需要的大部分内容。
Subclassing yyFlexLexer
Flex 允许您使用 %option yyclass
(或 --yyclass
command-line 选项)来指定子 class 的名称,它将代替 yyFlexLexer
定义yylex
。 Subclassing yyFlexLexer
允许您包含自己的 header,它定义了您的 subclass' 成员,甚至可能还有其他函数,以及它的构造函数;简而言之,如果您的意图只是用连续的标记填充 std::vector<alpha_token_t>
,您可以通过将 AlphaLexer
定义为 yyFlexLexer
的子 class 来轻松地做到这一点,其中一个名为 tokens
的实例成员(或者,也许,具有访问函数)。
您还可以向新的 class 添加额外的成员函数,这可能会提供您需要这些额外参数的功能。
虽然在 C 接口中使用 YY_DECL
宏可以很容易地完成,但不完全是 straight-forward 的事情是更改由生成的扫描函数的名称和原型柔性。可以做到(见下文),但不清楚它是否真正受支持。无论如何,对于 C++,它可能不太重要。
除了由 Flex 的 C++ classes [注 1] 的奇怪组织造成的小问题外,子 class 词法分析器 class 很简单。您需要从 yyFlexLexer
[注 2] 中导出 class,它在 FlexLexer.h
中声明,并且您需要告诉 Flex 您的 class 的名称是什么,要么通过在 Flex 文件中使用 %option yyclass
,或在命令行中使用 --yyclass
.
yyFlexLexer
包括用于操作输入缓冲区的各种方法,以及标准框架使用的词法扫描器的所有可变状态。 (其中大部分实际上是从基础 class FlexLexer
派生的。)它还包括一个带有原型
yylex
方法
virtual int yylex();
当您使用 subclass yyFlexLexer
时,yyFlexLexer::yylex()
被定义为通过调用 yyFlexLexer::LexerError(const char*)
发出错误信号并且生成的扫描器被定义为 class 定义为 yyclass
。 (如果不subclass,生成的scanner是yyFlexLexer::yylex()
。)
一个问题是您需要声明子class 的方式。通常,您会在 header 文件中这样做:
文件:myscanner.h(不要使用这个版本)
#pragma once
// DON'T DO THIS; IT WON'T WORK (flex 2.6)
#include <yyFlexLexer.h>
class MyScanner : public yyFlexLexer {
// whatever
};
然后您将 #include "myscanner.h"
在任何需要使用扫描仪的文件中,包括生成的扫描仪本身。
不幸的是,这不会起作用,因为它会导致 FlexLexer.h
在生成的扫描器中被包含两次; FlexLexer.h
没有通常意义上的包含保护,因为它被设计为被多次包含以支持 prefix
选项。所以需要定义两个header个文件:
文件:myscanner-internal.h
#pragma once
// This file depends on FlexLexer.h having already been included
// in the translation unit. Don't use it other than in the scanner
// definition.
class MyScanner : public yyFlexLexer {
// whatever
};
文件:myscanner.h
#pragma once
#include <FlexLexer.h>
#include "myscanner.h"
然后在每个需要了解扫描仪的文件中使用 #include "myscanner.h"
除了 扫描仪定义本身。在您的 myscanner.ll
文件中,您将 #include "myscanner-internal.h"
,这是有效的,因为 Flex 在插入扫描仪定义中的序言 C++ 代码之前已经包含 FlexLexer.h
。
更改 yylex 原型
你真的不能改变yylex
的原型(或名称),因为它是在FlexLexer.h
中声明的,而且如上所述,定义为发出错误信号。但是,您可以重新定义 YY_DECL
以创建 new 扫描仪界面。为此,您必须首先 #undef
现有的 YY_DECL
定义,至少在您的扫描仪定义中,因为带有 %option yyclass="MyScanner" contains
#define YY_DECL int MyScanner::yylex 的扫描仪(). That would make your
myscanner-internal.h` 文件看起来像这样:
#pragma once
// This file depends on FlexLexer.h having already been included
// in the translation unit. Don't use it other than in the scanner
// definition.
#undef YY_DECL
#define YY_DECL int MyScanner::alpha_yylex(std::vector<alpha_token_t>& tokens)
#include <vector>
#include "alpha_token.h"
class MyScanner : public yyFlexLexer {
public:
int alpha_yylex(std::vector<alpha_token_t>& tokens);
// whatever else you need
};
事实上 MyScanner
object 仍然有一个(不是很实用)yylex
方法可能不是问题。 FlexLexer
中有一些未记录的接口调用 yylex()
,但如果您不使用它们,这些都没有关系。 (无论如何,它们并不是那么有用。)但是您至少应该知道该接口存在。
无论如何,我不认为重命名 yylex
有什么意义(但也许您的审美不同)。它已经通过成为特定 class(MyScanner
,上文)的成员而有效地命名空间,因此 yylex
不会真正造成任何混淆。
在 std::vector<alpha_token_t>&
参数的特殊情况下,在我看来更简洁的解决方案是将引用作为成员变量放在 MyScanner
class 中并设置它与构造函数或访问器方法。除非您实际上在词法分析的不同点使用不同的向量——在您的问题的示例代码中并不明显——没有必要让每个调用站点都需要将向量的地址传递到 yylex
称呼。由于词法分析器操作是在 yylex
内部编译的,它是 MyScanner
的成员函数,实例变量——甚至是私有实例变量——在词法分析器操作中是可用的秒。当然,这不是额外 yylex
参数的唯一用例,但这是一个很常见的用例。
备注
根据生成代码中的注释,“C++ 接口一团糟”。
使用
%option prefix
,您可以根据需要将yy
更改为其他内容。这个功能据说是为了让你在同一个项目中包含多个词法扫描器。但是,如果您计划使用 subclassing,所有这些词法扫描器的基础 classes 将是相同的(除了它们的名称)。因此,具有不同的基 classes 几乎没有意义。使用%option prefix
重命名扫描仪 class 不如 subclassing 灵活且效率不高,并且它会产生额外的 header 并发症。 (有关详细信息,请参阅 this older answer。)所以我建议坚持使用 subclassing.