带有调试输出的 X3 解析器段错误 (BOOST_SPIRIT_X3_DEBUG) [英] X3 parser segfaults with debug output (BOOST_SPIRIT_X3_DEBUG)

查看:13
本文介绍了带有调试输出的 X3 解析器段错误 (BOOST_SPIRIT_X3_DEBUG)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这个问题涉及两个问题(如接受的答案所示),这两个问题都存在于版本中Boost Spirit X3 随 Boost 1.64 一起提供,但在撰写本文时 (2017-04-30),两者现在都已在开发分支中修复(或至少在编译时检测到).

This question touches on two issues (as shown by the accepted answer), both of which are present in the version of Boost Spirit X3 that ships with Boost 1.64, but both of which are now fixed (or at least detected at compile time) in develop branch at the time of writing (2017-04-30).

我更新了 mcve 项目,以反映我使用开发分支所做的更改最新的 boost 版本,希望它可以帮助其他面临类似问题的人.

I have updated the mcve project to reflect the changes that I made to use the develop branch instead of the latest boost release, in the hopes that it might help out others who face a similar issues.

<小时>

原来的问题

我正在尝试学习如何将 Spirit X3 解析器分解为单独的可重用语法,正如 示例代码(特别是 rexpr_full 和 calc)以及来自 CppCon 2015BoostCon.


The original question

I am trying to learn how to break up Spirit X3 parsers into separate reusable grammars, as encouraged by the example code (rexpr_full and calc in particular) and the presentations from CppCon 2015 and BoostCon.

我有一个符号表(基本上将不同的类型映射到我支持的类型的枚举类),我想在多个解析器中重用它.我能找到的唯一符号表示例是罗马数字示例,它位于单个源文件中.

I have a symbol table (essentially mapping different types to a enum class of the types I am supporting), which I would like to reuse in several parsers. The only example of symbol tables I could find is the roman numerals example, which is in a single source file.

当我尝试以更结构化示例的样式将符号表移动到其自己的 cpp/h 文件中时,如果我尝试解析不在符号表中的任何字符串,我的解析器将出现段错误.如果符号表是在与使用它的解析器相同的编译单元中定义的,则会引发预期异常(这是我所期望的).

When I try to move the symbol table into its own cpp/h file in the style of the more structured examples my parser will segfault if I try to parse any string which is not in the symbol table. If the symbol table is defined in the same compilation unit as the parsers that use it throws an expectation exception instead (which is what I would expect it to do).

定义了 BOOST_SPIRIT_X3_DEBUG 我得到以下输出:

With BOOST_SPIRIT_X3_DEBUG defined I get the following output:

<FruitType>
  <try>GrannySmith: Mammals</try>
  <Identifier>
    <try>GrannySmith: Mammals</try>
    <success>: Mammals</success>
    <attributes>[[
Process finished with exit code 11

我做了一个小项目,展示了我想要实现的目标,可以在这里找到:https://github.com/sigbjornlo/spirit_fruit_mcve

I have made a small project which shows what I am trying to achieve and is available here: https://github.com/sigbjornlo/spirit_fruit_mcve

我的问题:

  • 为什么在这种情况下将符号解析器移动到单独的编译单元会导致分段错误?
  • 使符号表可在多个解析器中重用的推荐方法是什么?(在 MCVE 中,我显然只在另一个解析器中使用 fruit 解析器,但在我的完整项目中,我想在其他几个解析器中使用它.)
  • Why does moving the symbol parser to a separate compilation unit cause a segmentation fault in this case?
  • What is the recommended way of making a symbol table reusable in multiple parsers? (In the MCVE I obviously only use the fruit parser in one other parser, but in my full project I want to use it in several other parsers.)

下面是 MCVE 项目的代码:

Below is the code for the MCVE project:

ma​​in.cpp

#include <iostream>

#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <boost/fusion/include/adapt_struct.hpp>

#include "common.h"
#include "fruit.h"

namespace ast {
    struct FruitType {
        std::string identifier;
        FRUIT fruit;
    };
}

BOOST_FUSION_ADAPT_STRUCT(ast::FruitType, identifier, fruit);

namespace parser {
    // Identifier
    using identifier_type = x3::rule<class identifier, std::string>;
    const auto identifier = identifier_type {"Identifier"};
    const auto identifier_def = x3::raw[x3::lexeme[(x3::alpha | '_') >> *(x3::alnum | '_')]];
    BOOST_SPIRIT_DEFINE(identifier);

    // FruitType
    struct fruit_type_class;
    const auto fruit_type = x3::rule<fruit_type_class, ast::FruitType> {"FruitType"};

    // Using the sequence operator creates a grammar which fails gracefully given invalid input.
    // const auto fruit_type_def = identifier >> ':' >> make_fruit_grammar();

    // Using the expectation operator causes EXC_BAD_ACCESS exception with invalid input.
    // Instead, I would have expected an expectation failure exception.
    // Indeed, an expectation failure exception is thrown when the fruit grammar is defined here in this compilation unit instead of in fruit.cpp.
    const auto fruit_type_def = identifier > ':' > make_fruit_grammar();

    BOOST_SPIRIT_DEFINE(fruit_type);
}

int main() {
    std::string input = "GrannySmith: Mammals";
    parser::iterator_type it = input.begin(), end = input.end();

    const auto& grammar = parser::fruit_type;
    auto result = ast::FruitType {};

    bool successful_parse = boost::spirit::x3::phrase_parse(it, end, grammar, boost::spirit::x3::ascii::space, result);
    if (successful_parse && it == end) {
        std::cout << "Parsing succeeded!
";
        std::cout << result.identifier << " is a kind of " << to_string(result.fruit) << "!
";
    } else {
        std::cout << "Parsing failed!
";
    }

    return 0;
}

std::string to_string(FRUIT fruit) {
    switch (fruit) {
        case FRUIT::APPLES:
            return "apple";
        case FRUIT::ORANGES:
            return "orange";
    }
}

common.h

#ifndef SPIRIT_FRUIT_COMMON_H
#define SPIRIT_FRUIT_COMMON_H

namespace x3 = boost::spirit::x3;

enum class FRUIT {
    APPLES,
    ORANGES
};

std::string to_string(FRUIT fruit);

namespace parser {
    using iterator_type = std::string::const_iterator;
    using context_type = x3::phrase_parse_context<x3::ascii::space_type>::type;
}

#endif //SPIRIT_FRUIT_COMMON_H

fruit.h

#ifndef SPIRIT_FRUIT_FRUIT_H
#define SPIRIT_FRUIT_FRUIT_H

#include <boost/spirit/home/x3.hpp>

#include "common.h"

namespace parser {
    struct fruit_class;
    using fruit_grammar = x3::rule<fruit_class, FRUIT>;

    BOOST_SPIRIT_DECLARE(fruit_grammar)

    fruit_grammar make_fruit_grammar();
}


#endif //SPIRIT_FRUIT_FRUIT_H

fruit.cpp

#include "fruit.h"

namespace parser {
    struct fruit_symbol_table : x3::symbols<FRUIT> {
        fruit_symbol_table() {
            add
                    ("Apples", FRUIT::APPLES)
                    ("Oranges", FRUIT::ORANGES);
        }
    };

    struct fruit_class;
    const auto fruit = x3::rule<fruit_class, FRUIT> {"Fruit"};
    const auto fruit_def = fruit_symbol_table {};
    BOOST_SPIRIT_DEFINE(fruit);

    BOOST_SPIRIT_INSTANTIATE(fruit_grammar, iterator_type, context_type);

    fruit_grammar make_fruit_grammar() {
        return fruit;
    }
}

推荐答案

非常好的复制器工作.这让我想起了我的 PR https://github.com/boostorg/spirit/pull/229(参见此处的分析奇怪的语义行为分裂后的提升精神x3).

Very good work on the reproducer. This reminded me of my PR https://github.com/boostorg/spirit/pull/229 (see analysis here Strange semantic behaviour of boost spirit x3 after splitting).

问题可能是静态初始化顺序 Fiasco 在规则初始化之前复制了规则的调试名称.

The problem would be Static Initialization Order Fiasco taking copies of the debug-names of rules before they became initialized.

实际上,确实禁用调试信息确实消除了崩溃,并且正确地抛出了预期失败.

In fact, indeed disabling the debug information does remove the crash, and correctly throws the expectation failure.

开发分支¹也会发生同样的情况,所以要么有另一个类似的事情,要么我错过了一个地方.现在,知道您可以禁用调试输出.如果找到位置,我会发布更新.

The same happens with the develop branch¹, so either there is another similar thing, or I missed a spot. For now, know you can disable the debug output. I'll post an update if I find the spot.

我没有错过任何地方.call_rule_definition 中有一个单独的问题,它使用实际属性类型而不是转换后的属性类型参数化 context_debug<> 帮助器类:

I didn't miss a spot. There's a separate issue in call_rule_definition where it parameterizes the context_debug<> helper class with the actual attribute type instead of the transformed one:

#if defined(BOOST_SPIRIT_X3_DEBUG)
                typedef typename make_attribute::type dbg_attribute_type;
                context_debug<Iterator, dbg_attribute_type>
                dbg(rule_name, first, last, dbg_attribute_type(attr_), ok_parse);
#endif

评论似乎表明这种行为是合乎需要的:它尝试在转换之前打印属性.但是,除非合成的属性类型与实际的属性类型匹配,否则它完全不起作用.在这种情况下,它使 context_debug 对临时转换的属性进行悬空引用,从而导致 未定义的行为.

The comment seems to suggest that this behaviour is as desired: it tries to print the attribute before transformation. However, it totally doesn't work unless the synthesized attribute type matches the actual attribute type. In this case, it makes context_debug take a dangling reference to a temporary converted attribute, leading to Undefined Behaviour.

实际上,在工作情况下,这也是未定义的行为.我只能假设在内联定义的情况下事情发生得很好,看起来事情就像预期的那样工作.

It's in fact also undefined behaviour in the working cases. I can only assume things happen to pan out nicely in the inline-definition case, making it seem like things work as intended.

据我所知,这将是一个干净的解决方案,可以防止任何无根据的转换以及随之而来的临时转换:

To the best of my knowledge this would be a clean fix, preventing any unwarranted conversions and the temporaries that come with them:

#if defined(BOOST_SPIRIT_X3_DEBUG)
                context_debug<Iterator, transform_attr>
                dbg(rule_name, first, last, attr_, ok_parse);
#endif

我为此创建了拉取请求:https://github.com/boostorg/精神/拉/232

I've created pull request for this: https://github.com/boostorg/spirit/pull/232

¹ develop 分支 似乎没有合并到 1.64 发布

¹ develop branch doesn't seem merged into the 1.64 release

这篇关于带有调试输出的 X3 解析器段错误 (BOOST_SPIRIT_X3_DEBUG)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆