c++ boost spirint x3 phrase_parse混淆

t2a7ltrp  于 2023-10-21  发布在  其他
关注(0)|答案(1)|浏览(99)

我将literal_string的输出定义为std::字符串:

namespace parsesss {
namespace x3 = boost::spirit::x3;
auto const literal_string = x3::rule<class literal_string,std::string>{"literal_string"};
auto const literal_string_def = x3::lexeme['"' >> +(x3::char_ - '"') >> '"'];

BOOST_SPIRIT_DEFINE(literal_string);
}

然后我用下面的代码进行了测试,结果也符合预期:

std::string test = "\"asdfjalsdjflajsdlfjalsdf\" \"xxxxxxxxxxasdfjalsdjflajsdlfjalsdf\"";
    std::vector<std::string> out;
    bool r = parse::x3::phrase_parse(test.begin(), test.end(), *(parsesss::literal_string), parse::x3::ascii::space, out);
    if(r && (parse::g_iter == parse::g_end)){
        std::cout << "parse success!" << std::endl;
    }else{
        std::cerr << "parse fail!" << std::endl;
    }

但是当我使用变量数据结构来存储输出时,std::字符串退化为字符,这是为什么?

struct constant : x3::variant<long,char,double,float,int>{
    using base_type::base_type;
    using base_type::operator=;
};
struct last : x3::variant<std::string,char,constant>{
    using base_type::base_type;
    using base_type::operator=;
};

//
    std::string test = "\"asdfjalsdjflajsdlfjalsdf\" \"xxxxxxxxxxasdfjalsdjflajsdlfjalsdf\"";
    std::vector<last> out;
    bool r = parse::x3::phrase_parse(test.begin(), test.end(), *(parsesss::literal_string), parse::x3::ascii::space, out);
    if(r && (parse::g_iter == parse::g_end)){
        std::cout << "parse success!" << std::endl;
    }else{
        std::cerr << "parse fail!" << std::endl;
    }

谁能告诉我为什么
或者在官方网站上哪里可以找到关于这方面的更详细的问题。

voj3qocg

voj3qocg1#

变量属性可以从char构造,这是首选。
如果从变量元素类型列表中删除char,它将工作:

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <iomanip>
#include <iostream>
namespace x3 = boost::spirit::x3;

namespace AST {
    using x3::variant;
    struct constant : variant<long, double, float, int> {
        using base_type::base_type;
        using base_type::operator=;
    };

    struct last : variant<std::string, constant> {
        using base_type::base_type;
        using base_type::operator=;
    };

    template <typename... T> static std::ostream& operator<<(std::ostream& os, variant<T...> const& v) {
        return boost::apply_visitor([&os](auto&& v) -> auto& { return os << v; }, v);
    }
}

namespace parsesss {
    auto const literal_string     = x3::rule<class literal_string, std::string>{"literal_string"};
    auto const literal_string_def = x3::lexeme['"' >> *~x3::char_('"') >> '"'];

    BOOST_SPIRIT_DEFINE(literal_string);
} // namespace parsesss

int main() {
    std::string const test = R"("asdf" "xxx yyy")";

    if (std::vector<AST::last> out;
        phrase_parse(test.begin(), test.end(), *(parsesss::literal_string) >> x3::eoi, x3::space, out))
        for (auto& el : out) {
            std::cout << " - " << el << std::endl;
        }
    else
        std::cout << "fail (" << quoted(test) << std::endl;
}

指纹

- asdf
 - xxx yyy

尝试次数

我试着用traits来暗示属性系统:

template <> struct x3::traits::is_substitute<AST::last, std::string> : std::true_type {};

但是,不出所料,这并没有奏效。

工作环境

你可以做的是破坏“char”的兼容性:

struct Char {
    char value;
    friend std::ostream& operator<<(std::ostream& os, Char ch) { return os << '\'' << ch.value << '\''; }
};

这已经工作了(live)。你可以在这里做得更短:(live

enum Char : char;

然而,我注意到,当您确实想解析字符字面量时,这些只会给您给予其他麻烦.

解决方案

所以,我把飞行向前,并制定了一些规则来匹配所有的AST节点。
根据经验,明确属性一致性是有帮助的。例如,constant的“naive”表达式将不起作用:

constant = x3::double_ | x3::int_;

原因有两个:

  • double_将急切地消耗整数。我们通过替换strict_real_policies来避免这个问题。
  • x3::int_doubleintmax_t兼容。现在,将有一个x3numeric parser,如x3::long_long,它恰好与intmax_t匹配,但最好是精确的
constant 
    = x3::real_parser<double, x3::strict_real_policies<double>>{}
    | x3::int_parser<intmax_t>{}
    ;

Live On Coliru

#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <iomanip>
#include <iostream>
namespace x3 = boost::spirit::x3;

namespace AST {
    using x3::variant;

    struct constant : variant<intmax_t, double> {
        using base_type::base_type;
        using base_type::operator=;
    };

    struct last : variant<std::string, char, constant> {
        using base_type::base_type;
        using base_type::operator=;
    };

    template <typename> constexpr std::string_view tname = "?";

    template <> constexpr std::string_view tname<std::string> = "string";
    template <> constexpr std::string_view tname<intmax_t>    = "integer";
    template <> constexpr std::string_view tname<double>      = "real";
    template <> constexpr std::string_view tname<char>        = "character";
    template <> constexpr std::string_view tname<constant>    = "constant";

    template <typename... T> static std::ostream& operator<<(std::ostream& os, variant<T...> const& v) {
        return boost::apply_visitor(
            [&os](auto const& v) -> auto& {
                auto type = tname<std::decay_t<decltype(v)>>;
                return os << type << '(' << v << ')';
            },
            v);
    }
}

namespace parsesss {
    auto string_lit = x3::rule<class string_lit, std::string>{"string_lit"} =
        x3::lexeme['"' >> *~x3::char_('"') >> '"'];
    auto char_lit = x3::rule<class char_lit, char>{"char_lit"} =
        x3::lexeme["'" >> ('\\' >> x3::char_ | ~x3::char_("'")) >> "'"];
    auto constant = x3::rule<class constant, AST::constant>{"constant"} =
        x3::real_parser<double, x3::strict_real_policies<double>>{} //
        | x3::int_parser<intmax_t>{};

    auto last = x3::rule<class last, AST::last>{"last"} =
        string_lit | char_lit | constant;
    
} // namespace parsesss

int main() {
    for (std::string const test :
         {
             R"("asdf" "xxx yyy")",
             R"("asdf" 'c' "xxx yyy")",
             R"("asdf" '\'' "xxx yyy")",
             R"("asdf" '\'' 3.14e4 9999 -9999 -inf +nan 3. "xxx yyy")",
         }) //
    {
        std::cout << "testcase " << quoted(test) << std::endl;
        std::vector<AST::last> out;
        if (phrase_parse(test.begin(), test.end(), *parsesss::last >> x3::eoi, x3::space, out))
            for (auto& el : out)
                std::cout << " - " << el << std::endl;
        else
            std::cout << " - FAIL" << std::endl;
    }
}

印刷

testcase "\"asdf\" \"xxx yyy\""
 - string(asdf)
 - string(xxx yyy)
testcase "\"asdf\" 'c' \"xxx yyy\""
 - string(asdf)
 - character(c)
 - string(xxx yyy)
testcase "\"asdf\" '\\'' \"xxx yyy\""
 - string(asdf)
 - character(')
 - string(xxx yyy)
testcase "\"asdf\" '\\'' 3.14e4 9999 -9999 -inf +nan 3. \"xxx yyy\""
 - string(asdf)
 - character(')
 - constant(real(31400))
 - constant(integer(9999))
 - constant(integer(-9999))
 - constant(real(-inf))
 - constant(real(nan))
 - constant(real(3))
 - string(xxx yyy)

相关问题