プログラミングなど

2009年5月20日水曜日

boost spirit ファイル解析エラー時情報

boost spirit でファイルを解析したときのエラー時情報出力の仕方について簡単な例があまりないようなので、調べたことを記述します。

下記のコードでは、1行にINT, REAL というキーワードと値の組み合わせを含んだファイルを読み込み、文法にエラーが存在した場合には、該当個所を出力します。

(正常な文法のファイルの例)
INT 100
REAL 10.0
INT 5
REAL 7.5

---- コード ----

#include <boost/spirit.hpp>
#include <iostream>
#include <iomanip>
#include <string>

// エラー出力のためのパーサ
struct error_parser {
 typedef boost::spirit::nil_t result_t;

 std::string msg_;
 error_parser(const char* msg) : msg_(msg) {}

 template <typename ScannerT>
 int operator()(const ScannerT& scan,
         result_t& result) const {
  if (!scan.at_end()) { // 入力が最後でない場合

   // スキャナの first には、ポジションイテレータが
   // 保持される
   boost::spirit::file_position fpos
    = scan.first.get_position();
   std::cout
    << "Error: " << fpos.file << " "
    << "Line("  << fpos.line  << ") "
    << "Column(" << fpos.column << ") "
    << msg_
    << std::endl;
  }
  return (-1);
 }
};
typedef boost::spirit::functor_parser<error_parser> error_p;
error_p syntax_error_p("Syntax is wrong.");
error_p keyword_error_p("Invalid Keyword.");
error_p value_error_p("Invalid Value.");

// 文法定義
struct test_grammar : boost::spirit::grammar<test_grammar> {
 template <typename ScannerT>
 struct definition {
  typedef boost::spirit::rule<ScannerT> rule_t;

  rule_t top_, line_,
      integer_statement_, real_statement_, other_statement_;

  definition(const test_grammar& self) {
   using namespace boost::spirit;

   top_ =
    +line_ | syntax_error_p;

   line_ =
    *blank_p >> (integer_statement_ |
           real_statement_  |
           other_statement_) >>
    ((*blank_p >> eol_p)               |
     ((anychar_p - blank_p - eol_p) >> value_error_p) |
     (+blank_p >> ((anychar_p - eol_p) >> syntax_error_p)));

   // "INT (整数)"
   integer_statement_ =
    str_p("INT") >>
    +blank_p >>
    (int_p | (anychar_p >> value_error_p));

   // "REAL (実数)"
   real_statement_ =
    (str_p("REAL") | keyword_error_p) >>
    +blank_p >>
    (real_p | (anychar_p >> value_error_p));

   // "それ以外"
   other_statement_ =
    anychar_p >> syntax_error_p;
  }
  const rule_t& start() const { return top_; }
 };
};

int main(int argc, char** argv) {
 std::string file = "sample.txt";

 // ファイルイテレータ
 typedef boost::spirit::file_iterator<> file_iterator_t;
 file_iterator_t file_first(file);
 if (!file_first) {
  std::cout << "cannot open file: " << file << std::endl;
  return (1);
 }

 // ポジションイテレータ
 typedef boost::spirit::position_iterator<file_iterator_t> position_iterator_t;
 position_iterator_t position_first(file_first,
                   file_first.make_end(),
                   file);
 position_iterator_t position_last;

 boost::spirit::parse_info<position_iterator_t> info =
  boost::spirit::parse(position_first,
             position_last,
             test_grammar());

 std::cout << std::boolalpha << info.full << std::endl;
 return (0);
}

================================

parse()関数の前に、ファイルイテレータ、位置情報イテレータを作成します。
ポジションイテレータは、 error_parser 内の operator()()関数内のscanで利用できます。

error_parserは、functor_parser<>によって、他の定義済パーサと同様にエラーパーサとして利用できるようにします。
definition()内にルールを記述する際、文法エラーの発生させたい個所に、エラーパーサを使用します。

尚、上記のコードでは、エラーの種類に応じて、出力メッセージを変更できるようにしています。

■参考
Spirit v1.6.0 - ファンクタパーサ
Spirit v1.6.0 - 詳細:スキャナ
boost ライブラリを使ってみる - エラー処理

0 件のコメント:

コメントを投稿