Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/amazon-s3/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ boost spirit报告语义错误_C++_Boost Spirit Qi - Fatal编程技术网

C++ boost spirit报告语义错误

C++ boost spirit报告语义错误,c++,boost-spirit-qi,C++,Boost Spirit Qi,我正在使用boost.spirit库,无法报告来自语义操作的简单错误消息 // supported parameter types (int or quoted strings) parameter = bsqi::int_ | bsqi::lexeme[L'"' > *(bsqi_coding::char_ - L'"') > L'"']; parameter.name("parameter"); // comma separator list of parameters (or

我正在使用boost.spirit库,无法报告来自语义操作的简单错误消息

// supported parameter types (int or quoted strings)
parameter = bsqi::int_ | bsqi::lexeme[L'"' > *(bsqi_coding::char_ - L'"') > L'"'];
parameter.name("parameter");

// comma separator list of parameters (or no parameters)
parameters = -(parameter % L',');
parameters.name("parameters");

// action with parameters
action = (Actions > L'(' > parameters > L')')[bsqi::_pass = boost::phoenix::bind(&ValidateAction, bsqi::_1, bsqi::_2)];
action.name("action");
Actions
只是一个符号表(
boost::spirit::qi::symbols
)。
parameters
的属性是描述参数类型的
boost::variant
std::vector
。我想在semantic action
ValidateAction
中生成一条有意义的错误消息,并指出输入中的错误位置。如果我只是将
\u pass
赋值为false,解析将结束,但错误消息类似于“预期”,而不是第二个参数的类型错误(预期为int而不是string)

我在某个地方读到,我可以从我的语义操作中抛出一个异常,但问题是,我没有找到是否以及如何从解析的值访问迭代器。例如,我想使用
expectation\u failure
exception,这样会自动调用我的错误处理程序,但我需要向异常传递迭代器,这似乎是不可能的


除了返回false之外,还有什么更好的方法可以用更详细的信息报告语义故障吗?

我会使用filepos\u迭代器并抛出一个异常,这样您就可以完全控制报告了

让我看看在剩下的15分钟里我能想出什么

好的,花了一点时间,但我认为这是一个很有启发性的演示:

#include <boost/fusion/adapted.hpp>
#include <boost/fusion/include/io.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/support_line_pos_iterator.hpp>
#include <boost/spirit/repository/include/qi_iter_pos.hpp>
#include <boost/lexical_cast.hpp>

namespace qi = boost::spirit::qi;
namespace qr = boost::spirit::repository::qi;
namespace px = boost::phoenix;
namespace qi_coding = boost::spirit::ascii;
using It = boost::spirit::line_pos_iterator<std::string::const_iterator>;

namespace ast {
    enum actionid { f_unary, f_binary };
    enum param_type { int_param, string_param };

    static inline std::ostream& operator<<(std::ostream& os, actionid id) {
        switch(id) {
            case f_unary:      return os << "f_unary";
            case f_binary:     return os << "f_binary";
            default:           return os << "(unknown)";
        } }
    static inline std::ostream& operator<<(std::ostream& os, param_type t) {
        switch(t) {
            case int_param:    return os << "integer";
            case string_param: return os << "string";
            default:           return os << "(unknown)";
        } }


    using param_value = boost::variant<int, std::string>;
    struct parameter {
        It position;
        param_value value;

        friend std::ostream& operator<<(std::ostream& os, parameter const& p) { return os << p.value; }
    };
    using parameters = std::vector<parameter>;

    struct action {
        /*
         *action() = default;
         *template <typename Sequence> action(Sequence const& seq) { boost::fusion::copy(seq, *this); }
         */
        actionid id;
        parameters params;
    };
}

namespace std {
    static inline std::ostream& operator<<(std::ostream& os, ast::parameters const& v) {
        std::copy(v.begin(), v.end(), std::ostream_iterator<ast::parameter>(os, " "));
        return os;
    }
}

BOOST_FUSION_ADAPT_STRUCT(ast::action, id, params)
BOOST_FUSION_ADAPT_STRUCT(ast::parameter, position, value)

struct BadAction : std::exception {
    It          _where;
    std::string _what;
    BadAction(It it, std::string msg) : _where(it), _what(std::move(msg)) {}
    It where() const { return _where; }
    char const* what() const noexcept { return _what.c_str(); }
};

struct ValidateAction {
    std::map<ast::actionid, std::vector<ast::param_type> > const specs {
        { ast::f_unary,  { ast::int_param } },
        { ast::f_binary, { ast::int_param, ast::string_param } },
    };

    ast::action operator()(It source, ast::action parsed) const {
        auto check = [](ast::parameter const& p, ast::param_type expected_type) {
            if (p.value.which() != expected_type) {
                auto name = boost::lexical_cast<std::string>(expected_type);
                throw BadAction(p.position, "Type mismatch (expecting " + name + ")");
            }
        };

        int i;
        try {
            auto& formals = specs.at(parsed.id);
            auto& actuals = parsed.params;
            auto  arity   = formals.size();

            for (i=0; i<arity; ++i)
                check(actuals.at(i), formals.at(i));

            if (actuals.size() > arity) 
                throw BadAction(actuals.at(arity).position, "Excess parameters");
        } catch(std::out_of_range const&) { 
            throw BadAction(source, "Missing parameter #" + std::to_string(i+1)); 
        }
        return parsed;
    }
};

template <typename It, typename Skipper = qi::space_type>
struct Parser : qi::grammar<It, ast::action(), Skipper> {
    Parser() : Parser::base_type(start) {
        using namespace qi;
        parameter  = qr::iter_pos >> (int_ | lexeme['"' >> *~qi_coding::char_('"') >> '"']);
        parameters = -(parameter % ',');
        action     = actions_ >> '(' >> parameters >> ')';
        start      = (qr::iter_pos >> action) [ _val = validate_(_1, _2) ];

        BOOST_SPIRIT_DEBUG_NODES((parameter)(parameters)(action))
    }
  private:
    qi::rule<It, ast::action(),     Skipper> start, action;
    qi::rule<It, ast::parameters(), Skipper> parameters;
    qi::rule<It, ast::parameter(),  Skipper> parameter;
    px::function<ValidateAction> validate_;

    struct Actions : qi::symbols<char, ast::actionid> {
        Actions() { this->add("f_unary", ast::f_unary)("f_binary", ast::f_binary); }
    } actions_;

};

int main() {
    for (std::string const input : {
            // good
            "f_unary( 0 )",
            "f_binary ( 47, \"hello\")",
            // errors
            "f_binary ( 47, \"hello\") bogus",
            "f_unary ( 47, \"hello\") ",
            "f_binary ( 47, \r\n      7) ",
        })
    {
        std::cout << "-----------------------\n";
        Parser<It> p;
        It f(input.begin()), l(input.end());

        auto printErrorContext = [f,l](std::ostream& os, It where) {
            auto line = get_current_line(f, where, l);

            os << " line:" << get_line(where) 
               << ", col:" << get_column(line.begin(), where) << "\n";
            while (!line.empty() && std::strchr("\r\n", *line.begin()))
                line.advance_begin(1);
            std::cerr << line << "\n";
            std::cerr << std::string(std::distance(line.begin(), where), ' ') << "^ --- here\n";
        };

        ast::action data;
        try {
            if (qi::phrase_parse(f, l, p > qi::eoi, qi::space, data)) {
                std::cout << "Parsed: " << boost::fusion::as_vector(data) << "\n";
            }
        } catch(qi::expectation_failure<It> const& e) {
            printErrorContext(std::cerr << "Expectation failed: " << e.what_, e.first);
        } catch(BadAction const& ba) {
            printErrorContext(std::cerr << "BadAction: " << ba.what(), ba.where());
        }

        if (f!=l) {
            std::cout << "Remaining unparsed: '" << std::string(f,l) << "'\n";
        }
    }
}
#包括
#包括
#包括
#包括
#包括
#包括
#包括
名称空间qi=boost::spirit::qi;
命名空间qr=boost::spirit::repository::qi;
名称空间px=boost::phoenix;
名称空间qi_coding=boost::spirit::ascii;
使用它=boost::spirit::line\u pos\u迭代器;
名称空间ast{
枚举操作ID{f_一元,f_二元};
枚举参数类型{int参数,字符串参数};
静态内联std::ostream&operatoradd(“f_一元,ast::f_一元)(“f_二进制,ast::f_二进制”);}
}行动;;
};
int main(){
对于(标准::字符串常量输入:{
//好
“f_一元(0)”,
“f_二进制(47,\'hello\'”,
//错误
“f_二进制(47,\“hello\”)伪”,
“f_一元(47,\'hello\'”,
“f_二进制(47,\r\n 7)”,
})
{

std::cout只是从草图中创建了一个SSCCE-稍后会再讨论这一点代码看起来与我的非常相似。我已经在
line_pos_iterator
中查看过,但它只是另一个迭代器的适配器。无论迭代器类型如何,仍然需要访问迭代器的实例或引用,然后我就可以访问这些实例或引用用于引发异常或格式化消息。在我的情况下,我始终使用一行作为输入,因此可以直接使用
std::distance(第一,当前)计算偏移量
但我没有找到如何在语义操作中访问
第一个
当前的
。我认为使用特殊的迭代器不是100%必需的。我期待着你的答案。好的,没有太多解释的呈现。如果你有问题,我将在大约4小时后出现。第2轮。现在更理智的代码样式。不再使用
phoenix::bind
。我现在已经完成了。很酷,thx很多。诀窍是添加这个
qr::iter\u pos
以添加迭代器作为属性。在我的代码中,我还将所有绑定属性类型(代码中的动作和参数)更改为模板,以接受任何迭代器,然后使用
BOOST\u FUSION\u ADAPT\u TPL\u STRUCT
(也许这有助于其他关注讨论的人)。
-----------------------
Parsed: (f_unary 0 )
-----------------------
Parsed: (f_binary 47 hello )
-----------------------
Expectation failed: <eoi> line:1, col:25
f_binary ( 47, "hello") bogus
                        ^ --- here
Remaining unparsed: 'f_binary ( 47, "hello") bogus'
-----------------------
BadAction: Excess parameters line:1, col:15
f_unary ( 47, "hello") 
              ^ --- here
Remaining unparsed: 'f_unary ( 47, "hello") '
-----------------------
BadAction: Type mismatch (expecting string) line:2, col:8
      7) 
      ^ --- here
Remaining unparsed: 'f_binary ( 47, 
      7) '