[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。
コメントの実装……のついでに、微妙に構造を書き換えてみました。
早速コードを。
#pragma warning(disable:4100) //引数は関数の本体部で 1 度も参照されません。
#pragma warning(disable:4512) //代入演算子を生成できません。#include <iostream>
#include <string>
#include <stack>#include <boost/spirit/home/qi.hpp>
#include <boost/spirit/home/phoenix.hpp>template<typename Iterator, typename Skipper>
struct Calc : boost::spirit::qi::grammar<Iterator, Skipper>{Calc() : Calc::base_type(line){
using namespace boost::spirit;
using namespace boost::spirit::arg_names;group = '(' >> expression[_val = _1] >> ')';
factor = lexeme[
int_[_val = _1] //数字
]
| group[_val = _1]; //部分式の演算結果
term = factor[_val = _1] >> *(('*' >> factor[_val *= _1])
|('/' >> factor[_val /= _1]));
expression = term[_val = _1] >> *(('+' >> term[_val += _1])
| ('-' >> term[_val -= _1]));line = expression[boost::phoenix::bind(&Calc::PUSH, this, _1)];
};boost::spirit::qi::rule<Iterator, int(), Skipper> expression;
boost::spirit::qi::rule<Iterator, int(), Skipper> term;
boost::spirit::qi::rule<Iterator, int(), Skipper> factor;
boost::spirit::qi::rule<Iterator, int(), Skipper> group;boost::spirit::qi::rule<Iterator, Skipper> line;
std::stack<int> stack;
bool isResult() const{
return !stack.empty();
}int GetResult(){
int i = stack.top();
Reset();
return i;
}void Reset(){
std::stack<int> empty;
std::swap(empty, stack);
}void PUSH(int i){
stack.push(i);
}
};
template<typename Iterator>
struct Comment : boost::spirit::qi::grammar<Iterator>{Comment() : Comment::base_type(comment){
using namespace boost::spirit;
using namespace boost::spirit::arg_names;comment = boost::spirit::ascii::space
| ("//" >> *(char_ - '\n') >> '\n')
| ("/*" >> *(char_ - "*/") >> "*/");
};boost::spirit::qi::rule<Iterator> comment;
};int main(void)
{
typedef Comment<std::string::const_iterator> SkipperType;
typedef Calc<std::string::const_iterator, SkipperType> ParserType;
SkipperType skipper;
ParserType parser;std::string str;
while (getline(std::cin, str))
{
if (str.empty() || str[0] == 'q' || str[0] == 'Q')
break;//一行コメントのテストのため、改行をつける
str.push_back('\n');std::string::const_iterator it = str.begin();
std::string::const_iterator end = str.end();bool result = phrase_parse(it, end, parser, skipper);
if (result && it == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << str << " Parses OK: " << std::endl;
if(parser.isResult()) std::cout << parser.GetResult() << std::endl;
}
else if(result)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << std::string(str.begin(), it) << " Parses OK: " << std::endl;
std::cout << std::string(it, end) << " amari" << std::endl;
if(parser.isResult()) std::cout << parser.GetResult() << std::endl;
}
else
{
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "-------------------------\n";
parser.Reset();
}
}return 0;
}
修正箇所その一。
template<typename Iterator, typename Skipper>
struct Calc : boost::spirit::qi::grammar<Iterator, Skipper>{
Stringじゃなくて、直接Iteratorをテンプレート引数に取るように。
また、以前boost::spirit::ascii::space_typeを指定していたところもSkipperというテンプレート引数に変更しました。
クラスの中身は電卓レベルまで戻しているので省略。
template<typename Iterator>
struct Comment : boost::spirit::qi::grammar<Iterator>{Comment() : Comment::base_type(comment){
using namespace boost::spirit;
using namespace boost::spirit::arg_names;comment = boost::spirit::ascii::space
| ("//" >> *(char_ - '\n') >> '\n')
| ("/*" >> *(char_ - "*/") >> "*/");
};boost::spirit::qi::rule<Iterator> comment;
};
ここがスキップパーサ本体です。
普通のパーサに、スキップしたい構文を指定するだけです。
今回は空白とC++風コメントにしてみました。
typedef Comment<std::string::const_iterator> SkipperType;
typedef Calc<std::string::const_iterator, SkipperType> ParserType;
SkipperType skipper;
ParserType parser;
何度も使い回す予定はないのですが、そのまま書くとわかりづらくなるのでtypedef。
Calcのテンプレート引数にSkipperTypeを渡しているのがポイントです。
残りは普通に使用しているだけなので割愛。
これでコメントが使えるようになりました。
そろそろきっちりと構文を決めないと……
//2009-10-25追記
ふと気になってパーサ内のmutableとconstの部分を直してみたら普通に動きました。
おもむろに勘違い……