/**/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /**/ #include #include #include #include /* **************************************************************************** * * * identity * * * **************************************************************************** */ template struct identity { typedef T type; }; /* **************************************************************************** * * * disable_as_copy * * * **************************************************************************** */ template struct disable_as_copy : identity { }; template struct disable_as_copy : std::enable_if< !std::is_base_of< typename std::remove_reference::type, typename std::remove_reference::type >::value > { }; /* **************************************************************************** * * * non_copyable * * * **************************************************************************** */ class non_copyable { protected: inline non_copyable(); inline ~non_copyable(); private: non_copyable(non_copyable&) = delete; non_copyable& operator=(non_copyable&) = delete; }; inline non_copyable::non_copyable() { } inline non_copyable::~non_copyable() { } /* **************************************************************************** * * * non_moveable * * * **************************************************************************** */ class non_moveable { protected: inline non_moveable(); inline ~non_moveable(); private: non_moveable(non_moveable&&) = delete; non_moveable& operator=(non_moveable&&) = delete; }; inline non_moveable::non_moveable() { } inline non_moveable::~non_moveable() { } /* **************************************************************************** * * * emplace_ptr * * * **************************************************************************** */ template class emplace_ptr { public: constexpr emplace_ptr(); template::type> inline emplace_ptr(U&&... args); emplace_ptr(const emplace_ptr& o); ~emplace_ptr(); auto operator=(const emplace_ptr& o) -> emplace_ptr&; operator typename identity::type() const; #define _(s, cv) \ inline cv T& operator*() cv, * operator->() cv, * get() cv; \ /**/ CHAOS_PP_EXPR(CHAOS_PP_SEQ_FOR_EACH( _, ()(const)(volatile)(const volatile) )) #undef _ private: T* p_; #ifndef __GNUC__ alignas(T) char b_[sizeof(T)]; #else char b_[sizeof(T)] __attribute__ ((__aligned__(__alignof__(T)))); #endif }; template constexpr emplace_ptr::emplace_ptr() : p_(nullptr) { } template template inline emplace_ptr::emplace_ptr(U&&... args) : p_(new (static_cast(b_)) T(std::forward(args)...)) { } template emplace_ptr::emplace_ptr(const emplace_ptr& o) : p_(o.p_ ? new (static_cast(b_)) T(*o.p_) : nullptr) { } template emplace_ptr::~emplace_ptr() { if (p_) { p_->~T(); } } template auto emplace_ptr::operator=(const emplace_ptr& o) -> emplace_ptr& { if (this != &o) { if (p_) { p_->~T(); p_ = nullptr; } p_ = o.p_ ? new (static_cast(b_)) T(*o.p_) : nullptr; } return *this; } template emplace_ptr::operator typename identity::type() const { return p_ ? &emplace_ptr::get : nullptr; } #define _(s, cv) \ template inline cv T& emplace_ptr::operator*() cv { \ return *operator->(); \ } \ template inline cv T* emplace_ptr::operator->() cv { \ return assert(p_), p_; \ } \ template inline cv T* emplace_ptr::get() cv { \ return p_; \ } \ /**/ CHAOS_PP_EXPR(CHAOS_PP_SEQ_FOR_EACH( _, ()(const)(volatile)(const volatile) )) #undef _ /* **************************************************************************** * * * input_iterator * * * **************************************************************************** */ template class input_iterator; template bool operator==(input_iterator i, input_iterator j) { return i.s_ == j.s_ && (i.p_.get() == nullptr) == (j.p_.get() == nullptr); } template inline bool operator!=(input_iterator i, input_iterator j) { return !(i == j); } template class input_iterator : public std::iterator { public: typedef typename T::value_type value_type; inline input_iterator(T& s); template inline input_iterator(T& s, U&&... args); inline auto operator*() const -> const value_type&; inline auto operator->() const -> const value_type*; inline auto operator++() -> input_iterator&; auto operator++(int) -> input_iterator; private: T* s_; emplace_ptr p_; friend bool operator==<>(input_iterator i, input_iterator j); }; template inline input_iterator::input_iterator(T& s) : s_(&s) { } template template inline input_iterator::input_iterator(T& s, U&&... args) : s_(&s), p_(std::forward(args)...) { } template inline auto input_iterator::operator*() const -> const value_type& { return *p_; } template inline auto input_iterator::operator->() const -> const value_type* { return p_.operator->(); } template inline auto input_iterator::operator++() -> input_iterator& { return *this = s_->begin(); } template auto input_iterator::operator++(int) -> input_iterator { input_iterator i(*this); ++*this; return i; } /* **************************************************************************** * * * buffer * * * **************************************************************************** */ template class buffer { public: typedef typename std::iterator_traits::value_type value_type; inline buffer(I first, I last); inline bool empty() const; auto pop() -> value_type; template inline void push(T&& v); private: std::stack s_; I f_, l_; }; template inline buffer::buffer(I first, I last) : f_(first), l_(last) { } template inline bool buffer::empty() const { return s_.empty() && f_ == l_; } template auto buffer::pop() -> value_type { if (!s_.empty()) { value_type v(s_.top()); s_.pop(); return v; } else { assert(f_ != l_); return *f_++; } } template template inline void buffer::push(T&& v) { s_.push(std::forward(v)); } /* **************************************************************************** * * * category_t * * * **************************************************************************** */ #define _(s, i, id) , id = 1u << i enum category_t : unsigned { eof CHAOS_PP_EXPR(CHAOS_PP_SEQ_FOR_EACH_I( _, (formal)(identifier)(number)(op_or_punc)(painted)(placemarker)(scanned)(special)(string)(stringized)(unhide)(unscanned)(whitespace) )) }; #undef _ #define _(s, op) \ constexpr category_t operator op(category_t a, category_t b) { \ return static_cast( \ static_cast(a) op static_cast(b) \ ); \ } \ /**/ CHAOS_PP_EXPR(CHAOS_PP_SEQ_FOR_EACH( _, (&)(|) )) #undef _ std::ostream& operator<<(std::ostream& o, category_t c) { #define _(s, c) \ case c: \ o << #c; \ break; \ /**/ switch (c) { CHAOS_PP_EXPR(CHAOS_PP_SEQ_FOR_EACH( _, (formal | scanned) (formal | stringized) (formal | unscanned) (identifier) (identifier | painted) (number) (op_or_punc) (op_or_punc | special) (placemarker) (string) (unhide) (whitespace) )) default: o << "invalid"; break; } #undef _ return o; } /* **************************************************************************** * * * definition * * * **************************************************************************** */ class definition; /* **************************************************************************** * * * token * * * **************************************************************************** */ class token { public: typedef std::map>::iterator iterator; inline token(category_t c, iterator i); inline category_t category() const; inline auto lookup() const -> iterator; private: category_t c_; iterator i_; }; inline token::token(category_t c, iterator i) : c_(c), i_(i) { } inline category_t token::category() const { return c_; } inline auto token::lookup() const -> iterator { return i_; } /* **************************************************************************** * * * definition * * * **************************************************************************** */ class definition { public: typedef std::vector::const_iterator iterator; template::type> inline definition(T&& rep); template definition(int arity, bool variadic, T&& rep); inline bool hidden() const; inline void hide(), unhide(); inline int arity() const; inline bool variadic() const; inline auto begin() const -> iterator, end() const -> iterator; inline category_t usage(int n) const; private: int a_; bool v_, h_; std::vector r_; std::vector u_; }; template inline definition::definition(T&& rep) : a_(-1), v_(false), h_(false), r_(std::forward(rep)), u_() { } template definition::definition(int arity, bool variadic, T&& rep) : a_((assert(0 <= arity), arity)), v_(variadic), h_(false), r_(std::forward(rep)), u_(a_) { for (const auto& i : r_) { if ((i.category() & formal) == formal) { int n = std::atoi(i.lookup()->first.c_str()); assert(0 <= n && n < a_); u_[n] = static_cast(u_[n] | i.category()); } } } inline bool definition::hidden() const { return h_; } inline void definition::hide() { assert(!h_); h_ = true; } inline void definition::unhide() { assert(h_); h_ = false; } inline int definition::arity() const { return a_; } inline bool definition::variadic() const { return v_; } #define _(s, id) \ inline auto definition::id() const -> iterator { \ return r_.id(); \ } \ /**/ CHAOS_PP_EXPR(CHAOS_PP_SEQ_FOR_EACH( _, (begin)(end) )) #undef _ inline category_t definition::usage(int n) const { assert(0 <= n && n < a_); return u_[n]; } /* **************************************************************************** * * * replacement_error * * * **************************************************************************** */ class replacement_error { public: template::type> replacement_error(T&&... args); inline const std::string& what() const; private: std::string w_; }; template inline replacement_error::replacement_error(T&&... args) : w_(std::forward(args)...) { } inline const std::string& replacement_error::what() const { return w_; } /* **************************************************************************** * * * table * * * **************************************************************************** */ std::map> table; #define _(s, id, text) \ auto id = table.insert(std::make_pair(text, nullptr)).first; \ /**/ CHAOS_PP_EXPR(CHAOS_PP_SEQ_FOR_EACH( _, (blank, "") (comma, ",") (left_parenthesis, "(") (newline, "\n") (right_parenthesis, ")") (space, " ") (stringizing, "#") (stringizing_alt, "%:") (token_paste, "##") (token_paste_alt, "%:%:") )) #undef _ /* **************************************************************************** * * * make_token * * * **************************************************************************** */ template inline token make_token(category_t c, T&&... args) { return token(c, table.insert(std::make_pair(std::string(std::forward(args)...), nullptr)).first); } /* **************************************************************************** * * * phase * * * **************************************************************************** */ template struct phase; /* **************************************************************************** * * * phase<3> * * * **************************************************************************** */ // HACK: This implementation simply returns a single "identifier" containing the characters in its input sequence. // This is sufficient for macro replacement except that it does not detect invalid pastes. template<> struct phase<3> { template class type; }; template class phase<3>::type { public: typedef std::array::const_iterator iterator; inline type(I first, I last); inline auto begin() const -> iterator, end() const -> iterator; private: std::array a_; }; template inline phase<3>::type::type(I first, I last) : a_ { { make_token(identifier, first, last) } } { } #define _(s, id) \ template inline auto phase<3>::type::id() const -> iterator { \ return a_.id(); \ } \ /**/ CHAOS_PP_EXPR(CHAOS_PP_SEQ_FOR_EACH( _, (begin)(end) )) #undef _ /* **************************************************************************** * * * paste * * * **************************************************************************** */ token paste(token a, token b) { if (a.category() == placemarker) { return b; } else if (b.category() == placemarker) { return a; } else { std::string juxtapose = a.lookup()->first + b.lookup()->first; bool flag = false; for (const auto& tok : phase<3>::type(juxtapose.begin(), juxtapose.end())) { if (flag) { throw replacement_error("token-pasting must yield a single token"); } a = tok; flag = true; } return a; } } /* **************************************************************************** * * * paste_all * * * **************************************************************************** */ template std::vector paste_all(I first, I last) { std::vector res; for (auto i = first; i != last; ++i) { if (i->category() == (op_or_punc | special) && (i->lookup() == token_paste || i->lookup() == token_paste_alt)) { for (auto j = res.rbegin(); j != res.rend(); ++j) { if (j->category() != whitespace) { res.erase(j.base(), res.end()); break; } } for (++i; i != last; ++i) { if (i->category() != whitespace) { break; } } if (res.empty() || i == last) { throw replacement_error("the token-pasting operator must be invoked with two operands"); } res.back() = paste(res.back(), *i); } else { res.push_back(*i); } } return res; } /* **************************************************************************** * * * scanner * * * **************************************************************************** */ template class scanner : non_copyable, non_moveable { public: typedef token value_type; typedef input_iterator iterator; inline scanner(I first, I last); auto begin() -> iterator; inline auto end() -> iterator; token get(); private: buffer b_; }; template inline scanner::scanner(I first, I last) : b_(first, last) { } template auto scanner::begin() -> iterator { token t = get(); return t.category() != eof ? iterator(*this, t) : end(); } template auto scanner::end() -> iterator { return iterator(*this); } template token scanner::get() { restart: if (b_.empty()) { return token(eof, blank); } token t = b_.pop(); switch (t.category()) { default: return t; case unhide: t.lookup()->second.get()->unhide(); goto restart; case identifier: if (definition* d = t.lookup()->second.get()) { if (d->hidden()) { return token(identifier | painted, t.lookup()); } else if (d->arity() == -1) { // object-like macro d->hide(); b_.push(token(unhide, t.lookup())); auto res = paste_all(d->begin(), d->end()); for (auto i = res.rbegin(); i != res.rend(); ++i) { if (i->category() != placemarker) { b_.push(*i); } } goto restart; } else { // function-like macro std::vector ws; while (!b_.empty()) { token u = b_.pop(); switch (u.category()) { default: b_.push(u); goto flush; case unhide: u.lookup()->second.get()->unhide(); break; case whitespace: ws.push_back(u); break; case op_or_punc: if (u.lookup() != left_parenthesis) { b_.push(u); goto flush; } else { if (d->arity() == 0) { // nullary while (!b_.empty()) { u = b_.pop(); switch (u.category()) { case unhide: u.lookup()->second.get()->unhide(); break; case whitespace: break; case op_or_punc: if (u.lookup() == right_parenthesis) { d->hide(); b_.push(token(unhide, t.lookup())); auto res = paste_all(d->begin(), d->end()); for (auto i = res.rbegin(); i != res.rend(); ++i) { if (i->category() != placemarker) { b_.push(*i); } } goto restart; } // fall... default: throw replacement_error("a nullary macro may not be invoked with arguments"); } } } else { // non-nullary std::vector> args(d->arity()); auto arg = args.begin(); int nesting = 0; while (!b_.empty()) { u = b_.pop(); if (u.category() == whitespace && u.lookup() == newline) { arg->push_back(token(whitespace, space)); } else if (u.category() != op_or_punc) { arg->push_back(u); } else if (u.lookup() == comma) { if (!nesting) { if (arg + 1 == args.end()) { if (!d->variadic()) { throw replacement_error("a function-like macro must be invoked with the correct number of arguments"); } arg->push_back(u); } else { ++arg; } } else { arg->push_back(u); } } else if (u.lookup() == left_parenthesis) { ++nesting; arg->push_back(u); } else if (u.lookup() != right_parenthesis) { arg->push_back(u); } else if (nesting) { --nesting; arg->push_back(u); } else { if (arg + 1 != args.end()) { throw replacement_error("a function-like macro must be invoked with the correct number of arguments"); } std::vector>> var; for (int i = 0; i != args.size(); ++i) { var.emplace_back(); if (!d->usage(i)) { // argument not used for (const auto& j : args[i]) { if (j.category() == unhide) { j.lookup()->second.get()->unhide(); } } } else { // argument used std::vector reset; if ((d->usage(i) & stringized) == stringized) { // stringized canonical form std::stringstream ss; bool ws = true; for (const auto& j : args[i]) { switch (j.category()) { case unhide: { definition* d = j.lookup()->second.get(); d->unhide(); reset.push_back(d); } break; case whitespace: if (!ws) { ss << ' '; ws = true; } break; default: ss << j.lookup()->first; ws = false; break; } } std::string s1 = ss.str(), s2 = "\""; if (!s1.empty() && *s1.rbegin() == ' ') { s1 = s1.substr(0, s1.size() - 1); } for (auto j : s1) { switch (j) { case '"': case '\\': s2 += '\\'; // fall... default: s2 += j; break; } } s2 += '"'; var.back()[formal | stringized] = std::vector { make_token(string, std::move(s2)) }; } if ((d->usage(i) & unscanned) == unscanned) { // unscanned (i.e. ## operand) canonical form for (auto j : reset) { j->hide(); } reset.clear(); auto& res = var.back()[formal | unscanned]; bool empty = true; for (const auto& j : args[i]) { switch (j.category()) { case unhide: { definition* d = j.lookup()->second.get(); d->unhide(); reset.push_back(d); } break; case identifier: if (definition* d = j.lookup()->second.get()) { res.push_back(d->hidden() ? token(identifier | painted, j.lookup()) : j); } else { res.push_back(j); } empty = false; break; default: empty = false; // fall... case whitespace: res.push_back(j); break; } } if (empty) { res = std::vector { token(placemarker, blank) }; } } if ((d->usage(i) & scanned) == scanned) { // scanned canonical form for (auto j : reset) { j->hide(); } auto& res = var.back()[formal | scanned]; for (const auto& j : scanner::const_iterator>(args[i].begin(), args[i].end())) { res.push_back(j); } } } } std::vector subst; for (const auto& i : *d) { switch (i.category()) { case formal | stringized: case formal | unscanned: case formal | scanned: { int n = std::atoi(i.lookup()->first.c_str()); assert(0 <= n && n < args.size()); for (const auto& j : var[n][i.category()]) { subst.push_back(j); } } break; case op_or_punc | special: if (i.lookup() == stringizing || i.lookup() == stringizing_alt) { throw replacement_error("the stringizing operator must be applied to a formal parameter"); } // fall... default: subst.push_back(i); break; } } d->hide(); b_.push(token(unhide, t.lookup())); auto res = paste_all(subst.begin(), subst.end()); for (auto i = res.rbegin(); i != res.rend(); ++i) { if (i->category() != placemarker) { b_.push(*i); } } goto restart; } } } throw replacement_error("a function-like macro invocation must be terminated with a right-parenthesis"); } } } flush: // left-parentheses not found for (auto i = ws.rbegin(); i != ws.rend(); ++i) { b_.push(*i); } return t; } } else { return t; } } } /* **************************************************************************** * * * define * * * **************************************************************************** */ template void define(T&& id, U&& rep) { table[id] = std::unique_ptr(new definition(std::forward(rep))); } template void define(T&& id, int arity, bool variadic, U&& rep) { table[id] = std::unique_ptr(new definition(arity, variadic, std::forward(rep))); } /* **************************************************************************** * * * main * * * **************************************************************************** */ int main(int argc, char* argv[]) try { try { #define T(seq) \ CHAOS_PP_EXPR(CHAOS_PP_SEQ_FOR_EACH_I( \ T_I, seq \ )) \ /**/ #define T_I(s, i, c, id) \ CHAOS_PP_COMMA_IF(i) make_token(c, id) \ /**/ #define OBJECT(id, seq) \ define(id, std::vector { T(seq) }); \ /**/ #define FUNCTION(id, arity, variadic, seq) \ define(id, arity, variadic, std::vector { T(seq) }); \ /**/ //std::cout << "#define CAT(a, ...) PRIMITIVE_CAT(a, __VA_ARGS__)\n"; FUNCTION( "CAT", 2, true, (identifier, "PRIMITIVE_CAT")(op_or_punc, "(")(formal | scanned, "0")(op_or_punc, ",")(whitespace, " ")(formal | scanned, "1")(op_or_punc, ")") ) //std::cout << "#define PRIMITIVE_CAT(a, ...) a ## __VA_ARGS__\n"; FUNCTION( "PRIMITIVE_CAT", 2, true, (formal | unscanned, "0")(whitespace, " ")(op_or_punc | special, "##")(whitespace, " ")(formal | unscanned, "1") ) //std::cout << "\n#define INC(x) PRIMITIVE_CAT(INC_, x)\n"; //std::cout << "#define INC_0 1\n"; //std::cout << "// ...\n"; //std::cout << "#define INC_9 9\n"; FUNCTION( "INC", 1, false, (identifier, "PRIMITIVE_CAT")(op_or_punc, "(")(identifier, "INC_")(op_or_punc, ",")(whitespace, " ")(formal | scanned, "0")(op_or_punc, ")") ) OBJECT("INC_0", (number, "1")) OBJECT("INC_1", (number, "2")) OBJECT("INC_2", (number, "3")) OBJECT("INC_3", (number, "4")) OBJECT("INC_4", (number, "5")) OBJECT("INC_5", (number, "6")) OBJECT("INC_6", (number, "7")) OBJECT("INC_7", (number, "8")) OBJECT("INC_8", (number, "9")) OBJECT("INC_9", (number, "9")) //std::cout << "\n#define DEC(x) PRIMITIVE_CAT(DEC_, x)\n"; //std::cout << "#define DEC_9 8\n"; //std::cout << "// ...\n"; //std::cout << "#define DEC_0 0\n"; FUNCTION( "DEC", 1, false, (identifier, "PRIMITIVE_CAT")(op_or_punc, "(")(identifier, "DEC_")(op_or_punc, ",")(whitespace, " ")(formal | scanned, "0")(op_or_punc, ")") ) OBJECT("DEC_0", (number, "0")) OBJECT("DEC_1", (number, "0")) OBJECT("DEC_2", (number, "1")) OBJECT("DEC_3", (number, "2")) OBJECT("DEC_4", (number, "3")) OBJECT("DEC_5", (number, "4")) OBJECT("DEC_6", (number, "5")) OBJECT("DEC_7", (number, "6")) OBJECT("DEC_8", (number, "7")) OBJECT("DEC_9", (number, "8")) //std::cout << "\n#define EXPR_S(s) PRIMITIVE_CAT(EXPR_, s)\n"; //std::cout << "#define EXPR_0(...) __VA_ARGS__\n"; //std::cout << "// ...\n"; //std::cout << "#define EXPR_9(...) __VA_ARGS__\n"; FUNCTION( "EXPR_S", 1, false, (identifier, "PRIMITIVE_CAT")(op_or_punc, "(")(identifier, "EXPR_")(op_or_punc, ",")(whitespace, " ")(formal | scanned, "0")(op_or_punc, ")") ) FUNCTION("EXPR_0", 1, true, (formal | scanned, "0")) FUNCTION("EXPR_1", 1, true, (formal | scanned, "0")) FUNCTION("EXPR_2", 1, true, (formal | scanned, "0")) FUNCTION("EXPR_3", 1, true, (formal | scanned, "0")) FUNCTION("EXPR_4", 1, true, (formal | scanned, "0")) FUNCTION("EXPR_5", 1, true, (formal | scanned, "0")) FUNCTION("EXPR_6", 1, true, (formal | scanned, "0")) FUNCTION("EXPR_7", 1, true, (formal | scanned, "0")) FUNCTION("EXPR_8", 1, true, (formal | scanned, "0")) FUNCTION("EXPR_9", 1, true, (formal | scanned, "0")) //std::cout << "\n#define SPLIT(i, ...) PRIMITIVE_CAT(SPLIT_, i)(, __VA_ARGS__)\n"; //std::cout << "#define SPLIT_0(p, a, ...) p ## a\n"; //std::cout << "#define SPLIT_1(p, a, ...) p ## __VA_ARGS__\n"; FUNCTION( "SPLIT", 2, true, (identifier, "PRIMITIVE_CAT")(op_or_punc, "(")(identifier, "SPLIT_")(op_or_punc, ",")(whitespace, " ")(formal | scanned, "0")(op_or_punc, ")") (op_or_punc, "(")(op_or_punc, ",")(whitespace, " ")(formal | scanned, "1")(op_or_punc, ")") ) FUNCTION( "SPLIT_0", 3, true, (formal | unscanned, "0")(whitespace, " ")(op_or_punc | special, "##")(whitespace, " ")(formal | unscanned, "1") ) FUNCTION( "SPLIT_1", 3, true, (formal | unscanned, "0")(whitespace, " ")(op_or_punc | special, "##")(whitespace, " ")(formal | unscanned, "2") ) //std::cout << "\n#define IS_VARIADIC(...) SPLIT(0, CAT(IS_VARIADIC_, IS_VARIADIC_C __VA_ARGS__))\n"; //std::cout << "#define IS_VARIADIC_C(...) 1\n"; //std::cout << "#define IS_VARIADIC_IS_VARIADIC_C 0,\n"; //std::cout << "#define IS_VARIADIC_1 1,\n"; FUNCTION( "IS_VARIADIC", 1, true, (identifier, "SPLIT")(op_or_punc, "(") (number, "0")(op_or_punc, ",") (whitespace, " ")(identifier, "CAT")(op_or_punc, "(") (identifier, "IS_VARIADIC_")(op_or_punc, ",") (whitespace, " ")(identifier, "IS_VARIADIC_C")(whitespace, " ")(formal | scanned, "0") (op_or_punc, ")") (op_or_punc, ")") ) FUNCTION("IS_VARIADIC_C", 0, false, (number, "1")) OBJECT("IS_VARIADIC_IS_VARIADIC_C", (number, "0")(op_or_punc, ",")) OBJECT("IS_VARIADIC_1", (number, "1")(op_or_punc, ",")) //std::cout << "\n#define NOT(x) IS_VARIADIC(PRIMITIVE_CAT(NOT_, x))\n"; //std::cout << "#define NOT_0 ()\n"; FUNCTION( "NOT", 1, false, (identifier, "IS_VARIADIC")(op_or_punc, "(")(identifier, "PRIMITIVE_CAT")(op_or_punc, "(")(identifier, "NOT_")(op_or_punc, ",")(whitespace, " ") (formal | scanned, "0")(op_or_punc, ")")(op_or_punc, ")") ) OBJECT("NOT_0", (op_or_punc, "(")(op_or_punc, ")")) //std::cout << "\n#define COMPL(b) PRIMITIVE_CAT(COMPL_, b)\n"; //std::cout << "#define COMPL_0 1\n"; //std::cout << "#define COMPL_1 0\n"; FUNCTION( "COMPL", 1, false, (identifier, "PRIMITIVE_CAT")(op_or_punc, "(")(identifier, "COMPL_")(op_or_punc, ",")(whitespace, " ")(formal | scanned, "0")(op_or_punc, ")") ) OBJECT("COMPL_0", (number, "1")) OBJECT("COMPL_1", (number, "0")) //std::cout << "\n#define BOOL(x) COMPL(NOT(x))\n"; FUNCTION( "BOOL", 1, false, (identifier, "COMPL")(op_or_punc, "(")(identifier, "NOT")(op_or_punc, "(")(formal | scanned, "0")(op_or_punc, ")")(op_or_punc, ")") ) //std::cout << "\n#define IIF(c) PRIMITIVE_CAT(IIF_, c)\n"; //std::cout << "#define IIF_0(t, ...) __VA_ARGS__\n"; //std::cout << "#define IIF_1(t, ...) t\n"; FUNCTION( "IIF", 1, false, (identifier, "PRIMITIVE_CAT")(op_or_punc, "(")(identifier, "IIF_")(op_or_punc, ",")(whitespace, " ")(formal | scanned, "0")(op_or_punc, ")") ) FUNCTION("IIF_0", 2, true, (formal | scanned, "1")) FUNCTION("IIF_1", 2, true, (formal | scanned, "0")) //std::cout << "\n#define IF(c) IIF(BOOL(c))\n"; FUNCTION( "IF", 1, false, (identifier, "IIF")(op_or_punc, "(")(identifier, "BOOL")(op_or_punc, "(")(formal | scanned, "0")(op_or_punc, ")")(op_or_punc, ")") ) //std::cout << "\n#define EMPTY()\n"; FUNCTION("EMPTY", 0, false,) //std::cout << "\n#define DEFER(id) id EMPTY()\n"; FUNCTION( "DEFER", 1, false, (formal | scanned, "0")(whitespace, " ")(identifier, "EMPTY")(op_or_punc, "(")(op_or_punc, ")") ) //std::cout << "\n#define OBSTRUCT(id) id DEFER(EMPTY)()\n"; FUNCTION( "OBSTRUCT", 1, false, (formal | scanned, "0")(whitespace, " ")(identifier, "DEFER")(op_or_punc, "(")(identifier, "EMPTY")(op_or_punc, ")")(op_or_punc, "(")(op_or_punc, ")") ) //std::cout << "\n#define EAT(...)\n"; FUNCTION("EAT", 1, true,) //std::cout << "\n#define REPEAT_S(s, n, m, ...) IF(n)(REPEAT_I, EAT)(, OBSTRUCT(), INC(s), DEC(n), m, __VA_ARGS__)\n"; //std::cout << "#define REPEAT_INDIRECT() REPEAT_S\n"; //std::cout << "#define REPEAT_I(p, _, s, n, m, ...) EXPR_S _(s)(REPEAT_INDIRECT _()(s, n, p ## m, p ## __VA_ARGS__) p ## m _(s, n, p ## __VA_ARGS__))\n"; FUNCTION( "REPEAT_S", 4, true, (identifier, "IF")(op_or_punc, "(")(formal | scanned, "1")(op_or_punc, ")")(op_or_punc, "(") (identifier, "REPEAT_I")(op_or_punc, ",")(whitespace, " ")(identifier, "EAT") (op_or_punc, ")")(op_or_punc, "(") (op_or_punc, ",") (whitespace, " ")(identifier, "OBSTRUCT")(op_or_punc, "(")(op_or_punc, ")")(op_or_punc, ",") (whitespace, " ")(identifier, "INC")(op_or_punc, "(")(formal | scanned, "0")(op_or_punc, ")")(op_or_punc, ",") (whitespace, " ")(identifier, "DEC")(op_or_punc, "(")(formal | scanned, "1")(op_or_punc, ")")(op_or_punc, ",") (whitespace, " ")(formal | scanned, "2")(op_or_punc, ",") (whitespace, " ")(formal | scanned, "3") (op_or_punc, ")") ) FUNCTION("REPEAT_INDIRECT", 0, false, (identifier, "REPEAT_S")) FUNCTION( "REPEAT_I", 6, true, (identifier, "EXPR_S")(whitespace, " ")(formal | scanned, "1")(op_or_punc, "(")(formal | scanned, "2")(op_or_punc, ")")(op_or_punc, "(") (identifier, "REPEAT_INDIRECT")(whitespace, " ")(formal | scanned, "1")(op_or_punc, "(")(op_or_punc, ")")(op_or_punc, "(") (formal | scanned, "2")(op_or_punc, ",") (whitespace, " ")(formal | scanned, "3")(op_or_punc, ",") (whitespace, " ")(formal | unscanned, "0")(whitespace, " ")(op_or_punc | special, "##")(whitespace, " ")(formal | unscanned, "4")(op_or_punc, ",") (whitespace, " ")(formal | unscanned, "0")(whitespace, " ")(op_or_punc | special, "##")(whitespace, " ")(formal | unscanned, "5") (op_or_punc, ")") (formal | unscanned, "0")(whitespace, " ")(op_or_punc | special, "##")(whitespace, " ")(formal | unscanned, "4") (whitespace, " ")(formal | scanned, "1")(op_or_punc, "(") (formal | scanned, "2")(op_or_punc, ",") (whitespace, " ")(formal | scanned, "3")(op_or_punc, ",") (whitespace, " ")(formal | unscanned, "0")(whitespace, " ")(op_or_punc | special, "##")(whitespace, " ")(formal | unscanned, "5") (op_or_punc, ")") (op_or_punc, ")") ) //std::cout << "\n#define COMMA() ,\n"; FUNCTION("COMMA", 0, false, (op_or_punc, ",")) //std::cout << "\n#define COMMA_IF(n) IF(n)(COMMA, EAT)()\n"; FUNCTION( "COMMA_IF", 1, false, (identifier, "IF")(op_or_punc, "(")(formal | scanned, "0")(op_or_punc, ")")(op_or_punc, "(") (identifier, "COMMA")(op_or_punc, ",") (whitespace, " ")(identifier, "EAT") (op_or_punc, ")")(op_or_punc, "(")(op_or_punc, ")") ) //std::cout << "\n#define A(s, i, id) COMMA_IF(i) template class id ## i\n"; //std::cout << "#define B(s, i, _) COMMA_IF(i) class\n"; FUNCTION( "A", 3, false, (identifier, "COMMA_IF")(op_or_punc, "(")(formal | scanned, "1")(op_or_punc, ")") (identifier, "template")(op_or_punc, "<") (identifier, "EXPR_S")(op_or_punc, "(")(formal | scanned, "0")(op_or_punc, ")")(op_or_punc, "(") (identifier, "REPEAT_S")(op_or_punc, "(") (formal | scanned, "0")(op_or_punc, ",") (whitespace, " ")(identifier, "INC")(op_or_punc, "(")(formal | scanned, "1")(op_or_punc, ")")(op_or_punc, ",") (whitespace, " ")(identifier, "B")(op_or_punc, ",") (whitespace, " ")(op_or_punc, "~") (op_or_punc, ")") (op_or_punc, ")") (op_or_punc, ">")(whitespace, " ")(identifier, "class")(whitespace, " ")(formal | unscanned, "2")(whitespace, " ")(op_or_punc | special, "##")(whitespace, " ")(formal | unscanned, "1") ) FUNCTION( "B", 3, false, (identifier, "COMMA_IF")(op_or_punc, "(")(formal | scanned, "1")(op_or_punc, ")") (whitespace, " ")(identifier, "class") ) //std::cout.put('\n'); // ----- std::cout << "EXPR_S(0)(REPEAT_S(0, 3, A, T))\n" << '\n'; // ----- std::vector input { T( (identifier, "EXPR_S")(op_or_punc, "(")(number, "0")(op_or_punc, ")")(op_or_punc, "(") (identifier, "REPEAT_S")(op_or_punc, "(") (number, "0")(op_or_punc, ",") (whitespace, " ")(number, "3")(op_or_punc, ",") (whitespace, " ")(identifier, "A")(op_or_punc, ",") (whitespace, " ")(identifier, "T") (op_or_punc, ")") (op_or_punc, ")") (whitespace, "\n") ) }; #undef T #undef T_I // ----- bool skip = true; std::function sep = [] () -> const char* { return ""; }; for (const token& t : scanner::const_iterator>(input.begin(), input.end())) { if (skip && t.category() == whitespace) { continue; } // (non-portable color-coding) std::cout << sep(); switch (t.category()) { case whitespace: std::cout << "\e[0;37m"; if (t.lookup()->first == " ") { std::cout << ""; } else if (t.lookup()->first == "\t") { std::cout << ""; } else if (t.lookup()->first == "\n") { std::cout << "\e[0m\n" << &std::flush; sep = [] () -> const char* { return ""; }; continue; } else { std::cout << ""; } std::cout << "\e[0m"; break; case identifier | painted: std::cout << "\e[0;34m"; // fall... default: std::cout << t.lookup()->first << "\e[0m"; break; } sep = [] () -> const char* { return " "; }; flush(std::cout); } std::cout.put('\n'); return 0; } catch (const replacement_error& e) { std::cerr << "\nerror: " << e.what() << '\n'; return 1; } } catch (...) { std::cerr << "\nerror: unknown internal error\n"; return 1; }