2016-11-16 13 views
2

私は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の属性は、std::vectorboost::variantであり、パラメータの種類を示します。意味的なエラーメッセージValidateActionの中に意味のあるエラーメッセージを生成したいと思います。 _passをfalseに設定した場合、解析は終了しますが、エラーメッセージは 'expecting'のようなものになります。 2番目のパラメータの型が間違っています(文字列の代わりに期待されるint)。

私はセマンティックアクションから例外をスローすることができますが、問題は、解析された値からイテレータにアクセスできるかどうか、どのようにアクセスできるかを見つけられないことです。例えば、エラーハンドラが自動的に呼び出されるように、私はexpectation_failure例外を使いたいと思っていましたが、不可能と思われる例外にイテレータを渡す必要があります。

セマンティックエラーを報告する方法はありますか?

答えて

3

私はfilepos_iteratorを使用して例外をスローするので、レポートを完全に制御できます。

もう少し時間がかかったが、それは有益デモだと思う、私は私が私が

[OK]を持って残り15分に思い付くことができるか見てみましょう:

Live On Coliru

#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"; 
     } 
    } 
} 

印刷:

----------------------- 
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) ' 
+0

スケッチからSSCCEを作成することができただけです。http://melpon.org/wandbox/permlink/YshtnalFMGZtzbak - これは後で戻ってきます – sehe

+0

コードは私のものとよく似ています。私はすでに 'line_pos_iterator'を見ましたが、別のイテレータへのアダプタです。イテレータのタイプにかかわらず、イテレータへのインスタンスまたは参照にアクセスする必要があります。イテレータを使用すると、例外をスローしたりメッセージをフォーマットしたりできます。私の場合は、常に1行の入力があるので、オフセットの計算は 'std :: distance(first、current)'で直接行うことができますが、セマンティックアクションで 'first'と' current'にどのようにアクセスできますか。私は特殊なイテレータを使うことは100%必要ではないと思います。私はあなたの答えをお待ちしています。 – bishopnator

+0

大丈夫です、あまり説明はありません。ご質問がある場合、私は約4時間で周りにいます。 [Live On Coliru](http://coliru.stacked-crooked.com/a/54cf7f41dce257b3) – sehe

関連する問題