C++ 如何从符号表中引用值?

C++ 如何从符号表中引用值?,c++,boost-spirit,boost-spirit-qi,C++,Boost Spirit,Boost Spirit Qi,我不知道如何将值从符号表传递到函数中 template <typename Iterator> class single_attribute_grammar : public qi::grammar<Iterator, AttributeData(), qi::blank_type> { public: single_attribute_grammar(const word_symbols &words) : single_attribute_gramma

我不知道如何将值从符号表传递到函数中

template <typename Iterator>
class single_attribute_grammar : public qi::grammar<Iterator, AttributeData(), qi::blank_type>
{
public:
    single_attribute_grammar(const word_symbols &words) : single_attribute_grammar::base_type(single_attribute_rule)
    {
        auto attr_word = phx::bind(&AttributeData::word, qi::_val);
        auto grammar_word = phx::bind(&WordGrammar::word, qi::_1);
        auto attr_value = phx::bind(&AttributeData::value, qi::_val);
        single_attribute_rule = qi::lexeme[words[attr_word = grammar_word] > 
            qi::int_[attr_value = qi::_1] > (qi::space|qi::eoi)] >>
            qi::eps(phx::bind(verify_range, qi::_r1, qi::_val)); // <-- HERE is the problem
        BOOST_SPIRIT_DEBUG_NODE(single_attribute_rule);
    }

private:
    qi::rule<Iterator, AttributeData(), qi::blank_type> single_attribute_rule;
};
如果有帮助,这里是MVCE

#include <iomanip>
#include <string>
#include <vector>

#include <boost/variant.hpp>
#include <boost/optional/optional.hpp>

#define BOOST_SPIRIT_DEBUG

#include <boost/config/warning_disable.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/qi_symbols.hpp>
#include <boost/spirit/include/phoenix_core.hpp>
#include <boost/spirit/include/phoenix_operator.hpp>
#include <boost/spirit/include/phoenix_object.hpp> // construct
#include <boost/spirit/include/support_multi_pass.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/phoenix/bind.hpp>

namespace qi = boost::spirit::qi;
namespace ascii = boost::spirit::ascii;
namespace classic = boost::spirit::classic;
namespace phx = boost::phoenix;
namespace fusion = boost::fusion;

struct AttributeData
{
    std::string word;
    int value;
};

using AttributeVariant = boost::variant<
    AttributeData
>;

struct WordGrammar
{
    std::string word;
    int range_from;
    int range_to;
};


BOOST_FUSION_ADAPT_STRUCT(
    AttributeData,
    (std::string, word)
    (int, value)
)

using word_symbols = qi::symbols<char, WordGrammar>;

bool verify_range(const WordGrammar &grammar, const AttributeData &data) 
{
    if(data.value < grammar.range_from || data.value > grammar.range_to)
    {
        return false;
    }

    return true;
}

template <typename Iterator>
class single_attribute_grammar : public qi::grammar<Iterator, AttributeData(), qi::blank_type>
{
public:
    single_attribute_grammar(const word_symbols &words) : single_attribute_grammar::base_type(single_attribute_rule)
    {
        auto attr_word = phx::bind(&AttributeData::word, qi::_val);
        auto grammar_word = phx::bind(&WordGrammar::word, qi::_1);
        auto attr_value = phx::bind(&AttributeData::value, qi::_val);
        single_attribute_rule = qi::lexeme[words[attr_word = grammar_word] > 
            qi::int_[attr_value = qi::_1] > (qi::space|qi::eoi)] >>
            qi::eps(phx::bind(verify_range, qi::_r1, qi::_val)); // <-- HERE is the problem
        BOOST_SPIRIT_DEBUG_NODE(single_attribute_rule);
    }

private:
    qi::rule<Iterator, AttributeData(), qi::blank_type> single_attribute_rule;
};

template <typename Iterator>
class all_attributes_grammar : public qi::grammar<Iterator, std::vector<AttributeVariant>(), qi::blank_type>
{
public:
    all_attributes_grammar(const word_symbols &words) : all_attributes_grammar::base_type(line_attribute_vec_rule)
    , sag(words)
    {
        line_attribute_rule = (
            sag
        );
        BOOST_SPIRIT_DEBUG_NODE(line_attribute_rule);
        line_attribute_vec_rule = (line_attribute_rule % *qi::blank) > qi::eoi;
        BOOST_SPIRIT_DEBUG_NODE(line_attribute_vec_rule);
    }

private:
    single_attribute_grammar<Iterator> sag;
    qi::rule<Iterator, AttributeVariant(), qi::blank_type> line_attribute_rule;
    qi::rule<Iterator, std::vector<AttributeVariant>(), qi::blank_type> line_attribute_vec_rule;
};

int main()
{
    std::vector<AttributeVariant> value;
    std::string data{"N100 X-100 AC5"};
    std::istringstream input(data);

    // iterate over stream input
    typedef std::istreambuf_iterator<char> base_iterator_type;
    base_iterator_type in_begin(input);

    // convert input iterator to forward iterator, usable by spirit parser
    typedef boost::spirit::multi_pass<base_iterator_type> forward_iterator_type;
    forward_iterator_type fwd_begin = boost::spirit::make_default_multi_pass(in_begin);
    forward_iterator_type fwd_end;

    // wrap forward iterator with position iterator, to record the position
    typedef classic::position_iterator2<forward_iterator_type> pos_iterator_type;
    pos_iterator_type position_begin(fwd_begin, fwd_end);
    pos_iterator_type position_end;

    word_symbols sym;
    sym.add
    ("N", {"N", 1, 9999})
    ("X", {"X", -999, 999})
    ("AC", {"AC", -99, 999})
    ;

    all_attributes_grammar<pos_iterator_type> all_attr_gr(sym);

    try
    {
        qi::phrase_parse(position_begin, position_end, all_attr_gr, qi::blank, value);
    }
    catch (const qi::expectation_failure<pos_iterator_type>& e)
    {
        const classic::file_position_base<std::string>& pos = e.first.get_position();
        std::cout <<
            "Parse error at line " << pos.line << " column " << pos.column << ":" << std::endl <<
            "'" << e.first.get_currentline() << "'" << std::endl <<
            std::setw(pos.column) << " " << "^- here" << std::endl;
    }

    return 0;
}
#包括
#包括
#包括
#包括
#包括
#定义BOOST_SPIRIT_调试
#包括
#包括
#包括
#包括
#包括
#包含//构造
#包括
#包括
#包括
#包括
名称空间qi=boost::spirit::qi;
名称空间ascii=boost::spirit::ascii;
名称空间classic=boost::spirit::classic;
名称空间phx=boost::phoenix;
名称空间融合=boost::fusion;
结构属性数据
{
字符串字;
int值;
};
使用AttributeVariant=boost::variant<
属性数据
>;
结构词语法
{
字符串字;
int范围从;
int范围_至;
};
增强融合适应结构(
属性数据,
(标准:字符串,单词)
(int,value)
)
使用单词_symbols=qi::symbols;
bool验证范围(常量字语法和语法、常量属性数据和数据)
{
if(data.valuegrammar.range_-to)
{
返回false;
}
返回true;
}
模板
类单属性语法:public qi::grammar
{
公众:
单属性语法(常量单词符号和单词):单属性语法::基本类型(单属性规则)
{
自动属性word=phx::bind(&AttributeData::word,qi::\u val);
自动语法\u word=phx::bind(&WordGrammar::word,qi:\u 1);
auto attr_value=phx::bind(&AttributeData::value,qi:_val);
单属性规则=qi::词素[words[attr\u word=grammar\u word]>
qi::int_[attr_value=qi::_1]>(qi::space | qi::eoi)]>>
qi::eps(phx::bind(验证_范围,qi:_r1,qi:_val));//qi::eoi;
BOOST\u-SPIRIT\u-DEBUG\u节点(行属性\u-vec\u规则);
}
私人:
单属性语法;
qi::规则行\属性\规则;
qi::规则行\属性\向量\规则;
};
int main()
{
std::向量值;
字符串数据{“N100X-100AC5”};
std::istringstream输入(数据);
//迭代流输入
typedef std::istreambuf_迭代器基迭代器_迭代器_类型;
基本迭代器输入开始(输入);
//将输入迭代器转换为前向迭代器,可由spirit解析器使用
typedef boost::spirit::多前向传递迭代器类型;
前向迭代器\u type fwd\u begin=boost::spirit::make\u default\u multi\u pass(in\u begin);
前向迭代器类型fwd\U end;
//使用位置迭代器包装前向迭代器,以记录位置
typedef classic::position_iterator 2 pos_iterator_type;
位置迭代器类型位置开始(前进开始,前进结束);
位置迭代器类型位置结束;
单词符号sym;
符号添加
(“N”,“N”,19999})
(“X”、{“X”、-999、999})
(“AC”,{“AC”,-99999})
;
所有属性语法所有属性gr(sym);
尝试
{
qi::短语解析(位置开始、位置结束、所有属性、qi::空白、值);
}
捕获(常数qi::预期\失败和e)
{
const classic::file_position_base&pos=e.first.get_position();

std::cout在对boost spirit进行了更多的研究之后,我发现解决方案是使用局部变量和继承属性:

template <typename Iterator>
class single_attribute_grammar : public qi::grammar<Iterator, AttributeData(), qi::locals<WordGrammar>, qi::blank_type>
{
public:
    single_attribute_grammar(const word_symbols &words) : single_attribute_grammar::base_type(single_attribute_rule)
    {
        auto attr_word = phx::bind(&AttributeData::word, qi::_val);
        auto grammar_word = phx::bind(&WordGrammar::word, qi::_1);
        auto attr_value = phx::bind(&AttributeData::value, qi::_val);
        word_rule = words;
        BOOST_SPIRIT_DEBUG_NODE(word_rule);
        range_check_rule = qi::eps(phx::bind(verify_range, qi::_r1, qi::_r2));
        BOOST_SPIRIT_DEBUG_NODE(range_check_rule);
        single_attribute_rule = qi::lexeme[word_rule[attr_word = grammar_word,qi::_a=qi::_1] >
            qi::int_[attr_value = qi::_1] > (qi::space|qi::eoi)] >>
            range_check_rule(qi::_a, qi::_val);
        BOOST_SPIRIT_DEBUG_NODE(single_attribute_rule);
    }

private:
    qi::rule<Iterator, WordGrammar()> word_rule;
    qi::rule<Iterator, void(const WordGrammar&, const AttributeData&), qi::blank_type> range_check_rule;
    qi::rule<Iterator, AttributeData(), qi::locals<WordGrammar>, qi::blank_type> single_attribute_rule;
};

很多事情需要简化

  • 当您使用语义动作时,为什么要修改AttributeData?(请参阅)
  • 在使用skipper时跳过空白要优雅得多。在逻辑上,在词素中跳过空白也是矛盾的(参见)
  • 特别是,此处跳过空白:

    line_attribute_vec_rule = (line_attribute_rule % *qi::blank) > qi::eoi;
    
    完全无效(因为空格已经被跳过,
    *qi::blank
    将永远不会匹配任何字符)。整个过程将简化为
    +行属性规则

    line_attribute_vec_rule = +line_attribute_rule > qi::eoi;
    
回复:你的答案是什么 事实上,您可以在规则中使用更多的状态。相反,我会简化AST以支持您的案例。例如:

struct AttrDef {
    std::string word;
    std::pair<int,int> range;
};

struct AttributeData {
    AttrDef def;
    int value;

    bool is_valid() const {
        return std::minmax({value, def.range.first, def.range.second}) == def.range;
    }
};

BOOST_FUSION_ADAPT_STRUCT(AttributeData, def, value)
正如您所见,没有状态是因为我没有使用eps。没错,我完全违背了我自己的指导原则(“避免语义动作”),原因很简单,它避免了显式状态(而不是使用exsting
qi:_pass

在main中进行大量简化(具体来说,使用
boost::spirit::istream_迭代器
,而不是滚动您自己的多过程调整),我将得出以下结论:

//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <iomanip>

namespace qi      = boost::spirit::qi;
namespace phx     = boost::phoenix;

struct AttrDef {
    std::string word;
    std::pair<int,int> range;
};

struct AttributeData {
    AttrDef def;
    int value;

    bool is_valid() const {
        return std::minmax({value, def.range.first, def.range.second}) == def.range;
    }
};

BOOST_FUSION_ADAPT_STRUCT(AttributeData, def, value)

static inline std::ostream& operator<<(std::ostream& os, const AttrDef& def) {
    return os << "AttrDef(" << def.word << ", " << def.range.first << ", " << def.range.second << ")";
}

using attr_defs = qi::symbols<char, AttrDef>;

template <typename Iterator>
struct single_attribute_grammar : public qi::grammar<Iterator, AttributeData()> {
    single_attribute_grammar(const attr_defs &defs)
        : single_attribute_grammar::base_type(start) 
    {
        using namespace qi;
        attribute_data = defs >> int_;
        start         %= attribute_data [ _pass = is_valid_(_1) ];

        BOOST_SPIRIT_DEBUG_NODES((attribute_data));
    }

private:
    phx::function<std::function<bool(AttributeData const&)> > is_valid_ {&AttributeData::is_valid};
    qi::rule<Iterator, AttributeData()> attribute_data;
    qi::rule<Iterator, AttributeData()> start;
};

using AttributeVariant = boost::variant<AttributeData>;

template <typename Iterator>
class all_attributes_grammar : public qi::grammar<Iterator, std::vector<AttributeVariant>(), qi::blank_type>
{
public:
    all_attributes_grammar(const attr_defs &defs)
        : all_attributes_grammar::base_type(line_attribute_vec_rule),
          sag(defs)
    {
        line_attribute_rule = (
            sag
        );
        line_attribute_vec_rule = +line_attribute_rule > qi::eoi;

        BOOST_SPIRIT_DEBUG_NODES((line_attribute_rule)(line_attribute_vec_rule));
    }

private:
    single_attribute_grammar<Iterator> sag;
    qi::rule<Iterator, AttributeVariant()>                              line_attribute_rule;
    qi::rule<Iterator, std::vector<AttributeVariant>(), qi::blank_type> line_attribute_vec_rule;
};

int main() {
    constexpr bool fail = false, succeed = true;
    struct _ { bool expect; std::string data; } const tests[] = {
         { succeed, "N100 X-100 AC5" },
         { fail,    ""               },
         { fail,    "  "             },
         { succeed, "N1"             },
         { succeed, " N1"            },
         { succeed, "N1 "            },
         { succeed, " N1 "           },
         { fail,    "N 1"            },
         { fail,    "N0"             },
         { succeed, "N9999"          },
         { fail,    "N10000"         },
    };

    for (auto test : tests) {
        std::istringstream input(test.data);

        typedef boost::spirit::classic::position_iterator2<boost::spirit::istream_iterator> pos_iterator_type;
        pos_iterator_type position_begin(boost::spirit::istream_iterator{input >> std::noskipws}, {}), position_end;

        attr_defs sym;
        sym.add
            ("N",  {"N", {1, 9999}})
            ("X",  {"X", {-999, 999}})
            ("AC", {"AC", {-99, 999}})
            ;

        all_attributes_grammar<pos_iterator_type> all_attr_gr(sym);

        try {
            std::vector<AttributeVariant> value;
            std::cout << " --------- '" << test.data << "'\n";

            bool actual = qi::phrase_parse(position_begin, position_end, all_attr_gr, qi::blank, value);

            std::cout << ((test.expect == actual)?"PASS":"FAIL");

            if (actual) {
                std::cout << "\t";
                for (auto& attr : value)
                    std::cout << boost::fusion::as_vector(boost::get<AttributeData>(attr)) << " ";
                std::cout << "\n";
            } else {
                std::cout << "\t(no valid parse)\n";
            }
        }
        catch (const qi::expectation_failure<pos_iterator_type>& e) {
            auto& pos = e.first.get_position();
            std::cout <<
                "Parse error at line " << pos.line << " column " << pos.column << ":" << std::endl <<
                "'" << e.first.get_currentline() << "'" << std::endl <<
                std::setw(pos.column) << " " << "^- here" << std::endl;
        }

        if (position_begin != position_end)
            std::cout << " -> Remaining '" << std::string(position_begin, position_end) << "'\n";
    }
}

事后思考:更基本的是,我想建议分离关注点:解析和验证是独立的步骤(将它们合并的唯一原因是当语法在上下文上不明确时,“无效的
单属性\u语法
”可能会作为其他东西成功解析).非常感谢!这给了我很多改进代码的想法。
struct AttrDef {
    std::string word;
    std::pair<int,int> range;
};

struct AttributeData {
    AttrDef def;
    int value;

    bool is_valid() const {
        return std::minmax({value, def.range.first, def.range.second}) == def.range;
    }
};

BOOST_FUSION_ADAPT_STRUCT(AttributeData, def, value)
using attr_defs = qi::symbols<char, AttrDef>;

template <typename Iterator>
struct single_attribute_grammar : public qi::grammar<Iterator, AttributeData()> {
    single_attribute_grammar(const attr_defs &defs)
        : single_attribute_grammar::base_type(start) 
    {
        using namespace qi;
        attribute_data = defs >> int_;
        start         %= attribute_data [ _pass = is_valid_(_1) ];

        BOOST_SPIRIT_DEBUG_NODES((attribute_data));
    }

private:
    phx::function<std::function<bool(AttributeData const&)> > is_valid_ {&AttributeData::is_valid};
    qi::rule<Iterator, AttributeData()> attribute_data;
    qi::rule<Iterator, AttributeData()> start;
};
//#define BOOST_SPIRIT_DEBUG
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/classic_position_iterator.hpp>
#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/io.hpp>
#include <iomanip>

namespace qi      = boost::spirit::qi;
namespace phx     = boost::phoenix;

struct AttrDef {
    std::string word;
    std::pair<int,int> range;
};

struct AttributeData {
    AttrDef def;
    int value;

    bool is_valid() const {
        return std::minmax({value, def.range.first, def.range.second}) == def.range;
    }
};

BOOST_FUSION_ADAPT_STRUCT(AttributeData, def, value)

static inline std::ostream& operator<<(std::ostream& os, const AttrDef& def) {
    return os << "AttrDef(" << def.word << ", " << def.range.first << ", " << def.range.second << ")";
}

using attr_defs = qi::symbols<char, AttrDef>;

template <typename Iterator>
struct single_attribute_grammar : public qi::grammar<Iterator, AttributeData()> {
    single_attribute_grammar(const attr_defs &defs)
        : single_attribute_grammar::base_type(start) 
    {
        using namespace qi;
        attribute_data = defs >> int_;
        start         %= attribute_data [ _pass = is_valid_(_1) ];

        BOOST_SPIRIT_DEBUG_NODES((attribute_data));
    }

private:
    phx::function<std::function<bool(AttributeData const&)> > is_valid_ {&AttributeData::is_valid};
    qi::rule<Iterator, AttributeData()> attribute_data;
    qi::rule<Iterator, AttributeData()> start;
};

using AttributeVariant = boost::variant<AttributeData>;

template <typename Iterator>
class all_attributes_grammar : public qi::grammar<Iterator, std::vector<AttributeVariant>(), qi::blank_type>
{
public:
    all_attributes_grammar(const attr_defs &defs)
        : all_attributes_grammar::base_type(line_attribute_vec_rule),
          sag(defs)
    {
        line_attribute_rule = (
            sag
        );
        line_attribute_vec_rule = +line_attribute_rule > qi::eoi;

        BOOST_SPIRIT_DEBUG_NODES((line_attribute_rule)(line_attribute_vec_rule));
    }

private:
    single_attribute_grammar<Iterator> sag;
    qi::rule<Iterator, AttributeVariant()>                              line_attribute_rule;
    qi::rule<Iterator, std::vector<AttributeVariant>(), qi::blank_type> line_attribute_vec_rule;
};

int main() {
    constexpr bool fail = false, succeed = true;
    struct _ { bool expect; std::string data; } const tests[] = {
         { succeed, "N100 X-100 AC5" },
         { fail,    ""               },
         { fail,    "  "             },
         { succeed, "N1"             },
         { succeed, " N1"            },
         { succeed, "N1 "            },
         { succeed, " N1 "           },
         { fail,    "N 1"            },
         { fail,    "N0"             },
         { succeed, "N9999"          },
         { fail,    "N10000"         },
    };

    for (auto test : tests) {
        std::istringstream input(test.data);

        typedef boost::spirit::classic::position_iterator2<boost::spirit::istream_iterator> pos_iterator_type;
        pos_iterator_type position_begin(boost::spirit::istream_iterator{input >> std::noskipws}, {}), position_end;

        attr_defs sym;
        sym.add
            ("N",  {"N", {1, 9999}})
            ("X",  {"X", {-999, 999}})
            ("AC", {"AC", {-99, 999}})
            ;

        all_attributes_grammar<pos_iterator_type> all_attr_gr(sym);

        try {
            std::vector<AttributeVariant> value;
            std::cout << " --------- '" << test.data << "'\n";

            bool actual = qi::phrase_parse(position_begin, position_end, all_attr_gr, qi::blank, value);

            std::cout << ((test.expect == actual)?"PASS":"FAIL");

            if (actual) {
                std::cout << "\t";
                for (auto& attr : value)
                    std::cout << boost::fusion::as_vector(boost::get<AttributeData>(attr)) << " ";
                std::cout << "\n";
            } else {
                std::cout << "\t(no valid parse)\n";
            }
        }
        catch (const qi::expectation_failure<pos_iterator_type>& e) {
            auto& pos = e.first.get_position();
            std::cout <<
                "Parse error at line " << pos.line << " column " << pos.column << ":" << std::endl <<
                "'" << e.first.get_currentline() << "'" << std::endl <<
                std::setw(pos.column) << " " << "^- here" << std::endl;
        }

        if (position_begin != position_end)
            std::cout << " -> Remaining '" << std::string(position_begin, position_end) << "'\n";
    }
}
 --------- 'N100 X-100 AC5'
PASS    (AttrDef(N, 1, 9999) 100) (AttrDef(X, -999, 999) -100) (AttrDef(AC, -99, 999) 5) 
 --------- ''
PASS    (no valid parse)
 --------- '  '
PASS    (no valid parse)
 -> Remaining '  '
 --------- 'N1'
PASS    (AttrDef(N, 1, 9999) 1) 
 --------- ' N1'
PASS    (AttrDef(N, 1, 9999) 1) 
 --------- 'N1 '
PASS    (AttrDef(N, 1, 9999) 1) 
 --------- ' N1 '
PASS    (AttrDef(N, 1, 9999) 1) 
 --------- 'N 1'
PASS    (no valid parse)
 -> Remaining 'N 1'
 --------- 'N0'
PASS    (no valid parse)
 -> Remaining 'N0'
 --------- 'N9999'
PASS    (AttrDef(N, 1, 9999) 9999) 
 --------- 'N10000'
PASS    (no valid parse)
 -> Remaining 'N10000'