我正在研究一个解析器组合库,我真的很喜欢我的解析器只是一个可调用的对象:
typedef std::function<parse_result(parse_stream)> parser;
这使得解析器组合器很好,例如:
parser operator &(parser a, parser b) { return both(a,b); }
但是我想要两个功能:
1)我希望自动将字符串文字提升为解析器,以便您可以执行以下操作:
parser option = "<" & regexp("[^+>]+");
2)我希望解析器有一个名称,我可以使用它来进行错误格式化。在上面的“both”解析器的情况下,我可以打印我预期的a.name()和b.name()例如。
到目前为止我尝试过的两个选项是
一个可调用的解析器类,这让我可以从字符串和std :: function实例构建,但一般的callable必须首先转换为std :: function并从那里转换为解析器,C ++不会进行两次隐式转换
从std :: function继承,所以我可以隐式转换函数,但是这似乎在将callables转换为解析器方面有很多问题。
有没有人对如何构建这个有什么想法?
答案 0 :(得分:3)
你不需要std函数的原始typedef;你的解析器不仅仅是一个std函数。
struct parser: std::function<parse_result(parse_stream)>{
using base = std::function<parse_result(parse_stream)>;
using base::base;
};
这应该允许
parser p = []( parse_stream str ) { return parse_result(7); };
因为我们使用继承构造函数来展示std::function
中的原始parser
ctors。
虽然您可以覆盖:
parser operator&(parser a, parser b) { return both(a,b); }
通过将&
放在parse_result
或parse_stream
的命名空间中,使用typedef版本,我建议反对它;在standarizatoin中有一个聊天来限制这种模板参数ADL。使用裸parser
类型,可以清楚地显示此类运算符重载的位置。
此外,某些类型不能在类之外重载,例如&=
。使用struct
,您可以在那里完成。
这些都没有修复
parser option = "<" & regexp("[^+>]+");
因为这里的问题是右手边不知道左手边做了什么(除非regexp
是一个函数撤回解析器)。
首先这样做:
struct parser: std::function<parse_result(parse_stream)>{
using base = std::function<parse_result(parse_stream)>;
parser( char const* str ):
base( [str=std::string(str)](parse_stream stream)->parse_result { /* do work */ } )
{}
parser( char c ):
base( [c](parse_stream str)->parse_result { /* do work */ } )
{}
using base::base;
};
然后你可以添加
namespace parsing {
// parser definition goes here
inline namespace literals {
inline parser operator""_p( char const* str ) { return str; }
}
}
和using namespace parsing::literals
表示"hello"_p
是一个尝试解析字符串"hello"
的解析器。