sak*_*ki7 0 c++ boost boost-spirit boost-spirit-qi boost-spirit-x3
Spirit X3如此“无状态”的目的是什么?
回顾Spirit V2,“语法”在许多方面在概念上都是有状态的。这是因为语法是类实例。
基本上,使语法(甚至任何一条规则)变得有状态有很多不良方面:
从理论上讲,添加外部状态会使您的语法变得不平凡。
相反,您可以说任何解析器都是有状态的(因为它解析当前上下文并且上下文是状态)。下面是程序员添加其他“上下文”的一个好例子:
quoted_string_ = as_string [omit [char_("'\"") [_a = _1]] >> *(char_ - lit(_a)) >> lit(_a)]
Run Code Online (Sandbox Code Playgroud)
qi::locals是非外部国家的好兆头。
程序员可以在语法中添加一些“外部状态”,并且在大多数情况下,它们只是在做错误的事情:
func_call_ = func_name_ >> lit('(') >> eps [ref(is_inside_function_call) = true] >> ...
Run Code Online (Sandbox Code Playgroud)
但是,在某些极端情况下,外部状态很有用。
macro_type_1_ =
lit("{{{") [PUSH_STATE(macro_ctx, Macro::Type1)] >> (
((any_expr_ - end_of_macro_ctx_) >> lit("}}}") >> eps [POP_STATE(macro_ctx)]) |
(eps [POP_STATE(macro_ctx)] >> eps [_pass = false])
)
;
macro_type_2_ =
lit("[[[") [PUSH_STATE(macro_ctx, Macro::Type2)] >> (
((any_expr_ - end_of_macro_ctx_) >> lit("]]]") >> eps [POP_STATE(macro_ctx)]) |
(eps [POP_STATE(macro_ctx)] >> eps [_pass = false])
)
;
Run Code Online (Sandbox Code Playgroud)
上面是一些任意上下文相关语言的示例。在这里,我通过为子规则模拟“析构函数”来添加“上下文堆栈”。这可能是使用Nabialec略施小的特殊变体的一个很好的例子end_of_macro_ctx_是qi::symbols实例。
(请参阅Boost.Spirit.Qi:在解析时动态创建“差异”解析器,以获取可能的实现细节)
您不能qi::locals在这里使用,因为不能保证的有效期qi::locals。因此,您应该使用全局变量(即语法类实例的成员变量)。
继承的属性?也许。如果您愿意将相同的变量传递给每个规则。
谈到外部状态,程序员可能还想在语法中增加一些基础知识。
on_error<fail>(root_, phx::bind(&my_logger, &MyLogger::error, _1, _2, _3, _4));
Run Code Online (Sandbox Code Playgroud)
您无法在X3上执行此操作。
X3期望用户使用自动常量实例在名称空间范围内定义他的每个规则。
好的,现在让我们看一下的实现BOOST_SPIRIT_DEFINE。它基本上只做一件事:
#定义BOOST_SPIRIT_DEFINE(您的规则,<未指定>)模板<未指定> <未指定> parse_rule(decltype(您的规则),<未指定> ...){<未指定>}
的第一个参数parse_rule()是decltype-d为给定规则的唯一类型。
这意味着两件事:
parse_rule()。parse_rule() 必须在名称空间范围内定义。您不能将实例的模板功能专用化。无法告诉X3使用我的任何实例变量。
我撒了谎。您可以根据需要执行以下操作:
static inline MyLogger& use_my_logger_please() {
static MyLogger instance; return instance;
}
Run Code Online (Sandbox Code Playgroud)
要么
#define MY_BOOST_SPIRIT_DEFINE(my_rule, <unspecified>, my_logger_f) <unspecified>
MY_BOOST_SPIRIT_DEFINE(rule_1_, ..., std::bind([] (MyLogger& l, std::string const& msg) { l << msg; }, this->logger_instance_, std::placeholders::_1))
Run Code Online (Sandbox Code Playgroud)
真?
您在“问题”文章中提出了许多未经证实的主张。
我认识到您的怒火中散发出的许多情感,但是当其中有很多值得商bat的地方时,我很难做出建设性的回应。
X3期望用户使用自动常量实例在名称空间范围内定义他的每个规则。
这是不正确的。X3不会那样做。可以说X3 促进了这种模式以启用诸如
另一方面,并不总是需要任何这些。
X3非常注重价值的特性使新模式得以实现。我非常喜欢能够执行以下操作:
auto make_parser(char delim) {
return lexeme [ delim >> *('\\' >> char_ | ~char_(delim)) >> delim ];
}
Run Code Online (Sandbox Code Playgroud)
确实,您可能需要“ x3 :: rule”来实现属性强制(例如qi :: transfom_attr):
auto make_parser(char delim) {
return rule<struct _, std::string> {} = lexeme [ delim >> *('\\' >> char_ | ~char_(delim)) >> delim ];
}
Run Code Online (Sandbox Code Playgroud)
实际上,我已经使用此模式进行了快速修改as<T>[]指令:了解Boost.Spirit中的列表运算符(%)。
auto make_parser(char delim) {
return as<std::string> [ lexeme [ delim >> *('\\' >> char_ | ~char_(delim)) >> delim ] ];
}
Run Code Online (Sandbox Code Playgroud)
没有什么可以阻止您使用动态解析器工厂那样从周围状态使用上下文。
语义动作是按值复制的,但是它们可以自由地引用外部状态。使用工厂功能时,它们可以再次使用周围状态。
快速创建状态的指令的唯一方法是扩展实际的上下文对象。该x3::with<>指令支持这一点,例如,具有可变因数升压精神X3不能编译重复指令
这可以用于限制无限数量的状态,例如,仅通过将(智能)指针/引用传递给解析器状态的边通道即可。
自定义解析器是获得X3强大功能的一种非常简单的方法。参见示例:
我个人认为自定义解析器比BOOST_SPIRIT_DECLARE / _DEFINE / _INSTANTIATE舞蹈更优雅。我承认我还没有在纯X3中创建任何需要多TU解析器的东西(我倾向于将X3用于小型,独立的解析器),但是从直觉上讲,我更喜欢从x3::parser_base提到的“祝福”宏中构建自己的TU分离逻辑。以上。另请参阅以下讨论:设计/结构X3解析器更像Qi解析器
编译器教程展示了如何使用规则标记类型的标记基类来触发特定规则的处理程序。我有一天想通了机制,但可惜我不记得所有的细节,LiveCoding.tv似乎失去了关于该主题的直播。
我鼓励您查看编译器示例(它们仅在源代码树中)。
我可以看到您如何注意到负面差异。重要的是要意识到X3不那么成熟,其目标是重量更轻,所以有些事情根本没有实现。还请注意,X3 以比以前更优雅的方式启用了许多功能。大多数事物与c ++ 14核心语言功能的交互更加自然,这是一大福音。
如果您想了解更多有关X3的令人失望的信息,请参阅该链接的答案中的入门性讨论,以及聊天中的一些讨论(如该讨论)。
希望我的反叛分子能帮助您学习X3。尽管我自由地承认有时我还是喜欢Qi,但我还是试图证明尽可能多的事情。