精神V2和X3的状态

时间:2017-08-02 10:10:36

标签: c++ boost boost-spirit boost-spirit-qi boost-spirit-x3

Spirit X3的目的是什么?无国籍'

'州的不良方面'在Spirit V2

回顾精神V2,"语法"在许多方面,是概念上有状态的。这是因为语法是一个类实例。

基本上,要使你的语法 - 甚至任何一条规则 - 都是有状态的,有很多不好的方面:

  • 这可能会使你的语法不可重入;
  • 它可能会给你的语法实例增加线程不安全性;
  • 自我管理的旗帜'是一场灾难。

从理论上讲,添加外部状态会使你的语法变得非常重要。

真的不需要州吗?

相比之下,您可以说任何解析器都是有状态的(因为它解析当前上下文而上下文是状态)。下面是一个很好的附加情境'由程序员添加:

quoted_string_ = as_string [omit [char_("'\"") [_a = _1]] >> *(char_ - lit(_a)) >> lit(_a)]

qi::locals - 外部状态的好兆头。

还有外部状态'一个程序员可以添加到他的语法,他们只是在大多数情况下做错了什么:

func_call_ = func_name_ >> lit('(') >> eps [ref(is_inside_function_call) = true] >> ...

但是,仍然存在外部状态有用的一些极端情况。

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])
    )
;

上面是一些任意上下文敏感语言的示例。在这里,我要添加一个'上下文堆栈'通过模拟“析构函数”来实现对于子规则。这可能是使用Nabialec Trick的特殊变体的好例子,其中end_of_macro_ctx_qi::symbols实例。

(有关可能的实施细节,请参阅Boost.Spirit.Qi: dynamically create "difference" parser at parse time

您无法在此使用qi::locals,因为无法保证qi::locals的生命周期。所以你应该使用一个全局变量(即你的语法类实例的成员变量)。

继承属性?也许。如果您愿意将相同的变量传递给每个规则。

语法本身的外部状态

谈到外部状态,程序员可能希望在语法中添加更多基本内容。

on_error<fail>(root_, phx::bind(&my_logger, &MyLogger::error, _1, _2, _3, _4));

你不能再在X3上这样做了。

X3的无国籍状态

X3期望用户在命名空间范围内定义他的每一条规则,并使用自动执行的实例。

好的,现在让我们来看看BOOST_SPIRIT_DEFINE的实施情况。它基本上只做一件事:

  

#define BOOST_SPIRIT_DEFINE(your_rule,&lt; unspecified&gt;)模板&lt;未指定&gt; &LT;未指定&GT; parse_rule(decltype(your_rule),&lt; unspecified&gt; ...){&lt; unspecified&gt; }

parse_rule()的第一个参数是decltype-d给定规则的唯一类型。

这意味着两件事:

  1. X3完全依赖于对parse_rule()的ADL调用。
  2. 必须在命名空间范围内定义
  3. parse_rule()
  4. 您无法专门为实例设置模板功能。没有办法告诉X3使用我的任何实例变量。

    我撒了谎。如果您愿意,可以这样做:

    static inline MyLogger& use_my_logger_please() {
        static MyLogger instance; return instance;
    }
    

    #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))
    

    真的?

1 个答案:

答案 0 :(得分:4)

您在“问题”文章中提出了许多未经证实的声明。

我认识到很多情绪在你的咆哮中闪耀,但我觉得很难在有太多争议的时候进行建设性的回应。

新的可能性

  

X3期望用户在命名空间范围内定义他的每一条规则,并使用自动执行的实例。

这根本不是真的。 X3不这样做。可以说X3 提升该模式以启用

等关键功能
  • 递归语法
  • 跨翻译单元分离解析器

另一方面,并​​不总是需要这些。

X3的价值导向使新模式能够实现目标。我非常喜欢能够做这样的事情:

有状态的分析器工厂

auto make_parser(char delim) {
     return lexeme [ delim >> *('\\' >> char_ | ~char_(delim)) >> delim ];
}

实际上,你可能“需要”x3 :: rule来实现属性强制(如qi :: transfom_attr):

auto make_parser(char delim) {
     return rule<struct _, std::string> {} = lexeme [ delim >> *('\\' >> char_ | ~char_(delim)) >> delim ];
}

事实上,我已经使用这种模式制作了快速而简单的as<T>[]指令:Understanding the List Operator (%) in Boost.Spirit

auto make_parser(char delim) {
     return as<std::string> [ lexeme [ delim >> *('\\' >> char_ | ~char_(delim)) >> delim ] ];
}

没有什么能阻止您使用这样的动态解析器​​工厂来使用来自周围状态的上下文。

有状态语义操作

语义操作按值复制,但可以自由引用外部状态。使用工厂功能时,他们可以再次使用周围状态。

有状态指令

动态创建状态的指令的唯一方法是扩展实际的上下文对象。 x3::with<>指令支持这一点,例如Boost Spirit X3 cannot compile repeat directive with variable factor

这可以用于无限量的状态,例如只需通过旁路将(智能)指针/引用传递给解析器状态。

自定义分析器

自定义解析器是在X3中获得大量功能的一种非常简单的方法。请参阅示例:

Spirit-Qi: How can I write a nonterminal parser?

我个人认为自定义解析器比BOOST_SPIRIT_DECLARE / _DEFINE / _INSTANTIATE舞蹈更优雅。我承认我从未在纯X3中创建任何需要多TU解析器的东西(我倾向于将X3用于小型,独立的解析器),但我直觉地更喜欢在x3::parser_base之上构建我自己的TU分离逻辑。上面提到的“祝福”宏。另见此讨论:Design/structure X3 parser more like Qi parser

错误/成功处理

编译器教程展示了如何使用规则标记类型的标记基类触发特定规则的处理程序。我有一天想出了机制,但遗憾的是我不记得所有的细节,LiveCoding.tv似乎已经失去了关于这个主题的直播。

我建议您查看编译器示例(它们仅在源树中)。

综述

我可以看到你如何发现负面差异。重要的是要意识到X3不太成熟,旨在更轻量级,所以有些事情根本没有实现。另请注意,X3以比以前更优雅的方式启用许多事物。大多数事物与c ++ 14核心语言功能更自然地交互这一事实是一个很大的好处。

如果您想详细了解X3令我失望的内容,请参阅that linked answer中的介绍性讨论,聊天中的一些讨论(如this one)。

我希望我的反击咆哮可以帮助你学习X3。我试图尽可能地证实尽可能多的东西,尽管我自愿承认我有时候仍然喜欢齐。