constexpr で Brainfuck
とりあえず、Sprout を使って書いてみました。
Sprout まじ Sprout。
あんまり効率とかは考えないでベタが出来ですが、入力から出力までをコンパイル時に行います。
そういえば constexpr で Brainfuck の実装は見かけたことがないような?
[ソース]
#include <sprout/string.hpp> #include <sprout/array.hpp> #include <sprout/iterator.hpp> #include <sprout/algorithm/fill.hpp> #include <sprout/operation.hpp> #include <sprout/range/algorithm/count.hpp> #include <boost/preprocessor/repetition/enum.hpp> #include <sprout/operation/set.hpp> #ifndef SOURCE_FILE # define SOURCE_FILE "brainfuck.txt" #endif // // static print // template<std::size_t Index, typename String> constexpr char at_c(String const& str){ return Index >= str.length() ? char('\0') : str[Index]; } template<char... cs> struct chars{}; #define STRING_MAX_LENGTH 16 #define DECL_AT_C(z, n, text) \ at_c<n>(text) #define TO_STRING_TYPE(str) \ chars<BOOST_PP_ENUM(STRING_MAX_LENGTH, DECL_AT_C, str)> #include <boost/mpl/print.hpp> template<typename String> void static_print(){ namespace m =boost::mpl; typedef typename m::print<String>::type output; } // // stringstream // template<std::size_t Size, typename String = sprout::string<Size>> struct basic_stringstream{ constexpr String const& str() const{ return string; } String string; }; typedef basic_stringstream<0> stringstream; template<std::size_t Lim> using stringstream_lim = basic_stringstream<Lim>; template< std::size_t N, typename String, typename Output, typename Result = basic_stringstream<N, String> > constexpr Result output(basic_stringstream<N, String> const& ss, Output const& output){ return { sprout::realign_to<String>(ss.string + output) }; } template<typename String, typename Output> constexpr auto output(basic_stringstream<0, String> const& ss, Output const& output) ->basic_stringstream<0, decltype(String{} + output)>{ return basic_stringstream<0, decltype(String{} + output)>{ ss.str() + output }; } template<std::size_t Size, typename String, typename T, std::size_t N> constexpr auto operator <<(basic_stringstream<Size, String> const& ss, T const(&str)[N]) ->decltype(output(ss, sprout::to_string(str))){ return output(ss, sprout::to_string(str)); } template< std::size_t Size, typename String, typename Source, typename = typename std::enable_if<sprout::is_string<Source>{}>::type > constexpr auto operator <<(basic_stringstream<Size, String> const& ss, Source const& str) ->decltype(output(ss, str)){ return output(ss, str); } template<std::size_t Size, typename String> constexpr auto operator <<(basic_stringstream<Size, String> const& ss, char c) ->decltype(output(ss, c)){ return output(ss, c); } template<std::size_t Size, typename String, typename Source> constexpr auto operator <<(basic_stringstream<Size, String> const& ss, Source const& str) ->decltype(output(ss, sprout::to_string(str))){ return output(ss, sprout::to_string(str)); } // // brainfuck // template<typename Iter, typename Char = typename std::iterator_traits<Iter>::value_type > constexpr Iter find_scope_end(Iter first, Iter last, std::size_t count = 0){ return first == last ? first : *first == Char('[') ? find_scope_end(sprout::next(first), last, count+1) : *first == Char(']') && count == 0 ? first : *first == Char(']') ? find_scope_end(sprout::next(first), last, count-1) : find_scope_end(sprout::next(first), last, count); } template<typename Iter, typename Char = typename std::iterator_traits<Iter>::value_type > constexpr Iter find_scope_start(Iter first, Iter last, std::size_t count = 0){ return first == last ? last : *last == Char(']') ? find_scope_start(first, sprout::prev(last), count+1) : *last == Char('[') && count == 0 ? last : *last == Char('[') ? find_scope_start(first, sprout::prev(last), count-1) : find_scope_start(first, sprout::prev(last), count); } template< typename Iter, typename OStream, typename Buffer, typename InputIter, typename Char = typename std::iterator_traits<Iter>::value_type > constexpr OStream brainfuck( Iter first, Iter mid, Iter last, OStream const& os, Buffer const& buffer, InputIter input, std::size_t ptr = 0 ){ namespace s = sprout; typedef s::reverse_iterator<Iter> reverse_iterator; return mid == last ? os : *mid == Char('>') ? brainfuck(first, s::next(mid), last, os, buffer, input, ptr+1) : *mid == Char('<') ? brainfuck(first, s::next(mid), last, os, buffer, input, ptr-1) : *mid == Char('+') ? brainfuck(first, s::next(mid), last, os, s::set(buffer, ptr, Char(buffer[ptr]+1)), input, ptr) : *mid == Char('-') ? brainfuck(first, s::next(mid), last, os, s::set(buffer, ptr, Char(buffer[ptr]-1)), input, ptr) : *mid == Char('.') ? brainfuck(first, s::next(mid), last, os << buffer[ptr], buffer, input, ptr) : *mid == Char(',') ? brainfuck(first, s::next(mid), last, os, s::set(buffer, ptr, Char(*input)), s::next(input), ptr) : *mid == Char('[') && buffer[ptr] == 0 ? brainfuck(first, s::next(find_scope_end(s::next(mid), last)), last, os, buffer, input, ptr) : *mid == Char(']') && buffer[ptr] != 0 ? brainfuck(first, s::next(find_scope_start(first, s::prev(mid))), last, os, buffer, input, ptr) : brainfuck(first, s::next(mid), last, os, buffer, input, ptr); } int main(){ // // ファイルから Brainfuck のコードを読み込み // { static constexpr auto source = sprout::to_string( #include SOURCE_FILE ); static constexpr auto buffer_size = 32; // static constexpr auto output_size = sprout::range::count(source, '.'); static constexpr auto output_size = 32; static constexpr auto buffer = sprout::fill(sprout::array<char, buffer_size>{}, char{0}); static constexpr auto input = sprout::to_string(""); static constexpr stringstream_lim<output_size> ss{}; static constexpr auto result = brainfuck( sprout::begin(source), sprout::begin(source), sprout::end(source), ss, buffer, sprout::begin(input) ); static_print<TO_STRING_TYPE(result.str())>(); } // // 入力値を渡して加算 // { static constexpr auto source = sprout::to_string( R"delimiter( ,>,>+<< >[-<+>]< ++++++++++++++++++++++++++++++++++++++++++++++++. )delimiter" ); static constexpr auto buffer_size = 32; static constexpr auto output_size = 32; static constexpr auto buffer = sprout::fill(sprout::array<char, buffer_size>{}, char{0}); static constexpr auto input = sprout::make_common_array(2, 5); static constexpr stringstream_lim<output_size> ss{}; static constexpr auto result = brainfuck( sprout::begin(source), sprout::begin(source), sprout::end(source), ss, buffer, sprout::begin(input) ); static_print<TO_STRING_TYPE(result.str())>(); } return 0; }
[brainfuck.txt]
R"delimiter( >+++++++++[<++++++++>-]<. >+++++++[<++++>-]<+. +++++++. . +++. [-]>++++++++[<++++>-]<. >+++++++++++[<+++++>-]<. >++++++++[<+++>-]<. +++. ------. --------. [-]> ++++++++[<++++>-]<+. [-]++++++++++. )delimiter"
[出力]
In file included from brainfuck\main.cpp|38| 0: || D:/boost/boost_1_49_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<chars<'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\012', '\000', '\000', '\000'> >': main.cpp|44 col 42| required from 'void static_print() [with String = chars<'H', 'e', 'l', 'l', 'o', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\012', '\000', '\000', '\000'>]' main.cpp|203 col 46| required from here \home\work\software\lib\cpp\boost\boost_1_49_0\boost\mpl\print.hpp|55 col 10| warning: comparison between signed and unsigned integer expressions [-Wsign-compare] || enum { || ^ || D:/boost/boost_1_49_0/boost/mpl/print.hpp: In instantiation of 'struct boost::mpl::print<chars<'7', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000'> >': main.cpp|44 col 42| required from 'void static_print() [with String = chars<'7', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000', '\000'>]' main.cpp|233 col 46| required from here \boost\mpl\print.hpp|55 col 10| warning: comparison between signed and unsigned integer expressions [-Wsign-compare] || enum { || ^
C++ のソースと同じディレクトリに brainfuck.txt を作っておくと、それが読みこまれて結果が出力されます。
まぁ #include "brainfuck.txt" とかしているだけなんですが。
残念ながらバッファのサイズは今のところ固定ですね…。
一応、評価時に値を渡すことで入力にも対応していたりします。
(後半の加算処理がそれ。
あと "Hello, World!" の出力とかは -fconstexpr-depth=4096 とかオプションを指定しておかないとコンパイルエラーになります☆(ゝω・)vキャピ
もうちょっと拡張性を高くして、派生言語にも対応してみたい。
Sprout.Weed 使ったバージョンも作ってみたいけど…どうなんだろうか。