boost::iostreams あたりを使って

前々回のプログラムを、今回はもうちょっとまともに書き直して見ます。
その時、ちょっと興味のあった boost::iostreams::output_filter の機能を使って試してみましょうかね。


では、さっそく:

#include <iostream>
#include <string>
#include <boost/xpressive/xpressive.hpp>

#include <boost/iostreams/filtering_stream.hpp>
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/operations.hpp>
#include <boost/iostreams/copy.hpp>

using namespace std;
using namespace boost::xpressive;
using namespace boost::iostreams;

// HTML文字参照を行うフィルタ
#include <map>
class char_ref : public output_filter
{
public:
	typedef map< int, const char* > list_type;

	char_ref( const list_type& ls )
		: ref_list(ls)
	{
	}

	// 出力本体
	template<typename Sink>
	bool put( Sink& snk, int c )
	{
		list_type::const_iterator ite = ref_list.find(c); 
		if( ite == ref_list.end() )
		{
			// 文字参照リストに含まれていなかったら、そのまま出力
			return boost::iostreams::put( snk, c );
		}
		else
		{
			// 文字参照リストに含まれていたら、その文字列を出力
			const char* p = (*ite).second;

			while( *p )
			{
				boost::iostreams::put( snk, *(p++) );
			}

			return true;
		}
	}

	// 文字参照リスト
	const list_type& ref_list;
};

ostream& cpp_decoration( ostream& os, string src /* 明示的にコピーするの面倒なので、最初から値で */ )
{
	sregex c_ = ("/*" >> -*_ >> "*/") | 	// C コメント
				("//" >> *(~_n) );	// C++ コメント
	sregex comment = c_ >> *( *space >> c_ );
	
	// クォートされた文字列。( '&' >> -*_ >> ';' ) は「文字参照一文字」
	sregex quoted = ( ~after('\\') >> '"' >> -*_ >> ~after('\\') >> *(as_xpr("\\\\")) >> '"') |
			( ~after('\\') >> '\'' >> !as_xpr('\\') >> ( _ | '&' >> -*_ >> ';' ) >> '\'' );

	smatch m;

	// コメントか、文字列(最初に出てきたほう)を検索
	while( regex_search( src, m, ( quoted | comment ) ) )
	{
		// とりあえず、出現までは普通に流して
		os << m.prefix().str();

		// マッチの最初の文字で、どっちかを判定
		if( m.str()[0] == '/' )
		{
			// コメント
			os << "<span class=comment>" << m.str() << "</span>";
		}
		else
		{
			// 文字列
			os << "<span class=quoted>" << m.str() << "</span>";
		}

		// 残りの文字列を、改めて検索対象に
		src = m.suffix().str();
	}

	// 見つからなかったので、そのまま出力
	return os << src;
}

int main()
{
	ostringstream buf;
	filtering_ostream sink;

	// 文字参照リストを作って
	char_ref::list_type ls;

	// 格納
	ls['&'] = "&amp;";
	ls['<'] = "&lt;";
	ls['>'] = "&gt;";

	sink.push( char_ref(ls) );
	sink.push( buf );

	// 折角なので boost で
	copy( cin, sink );

	cpp_decoration( cout, buf.str() );

	return 0;
}

こんな感じで、どうでしょうか。


解説は・・・面倒なので気が向いたら今度。