/**/ #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; /* **************************************************************************** * * * 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()->first == "##" || i->lookup()->first == "%:%:")) { 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 make_token(eof, ""); } 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()->first != "(") { 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()->first == ")") { 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()->first == "\n") { arg->push_back(make_token(whitespace, " ")); } else if (u.category() != op_or_punc) { arg->push_back(u); } else if (u.lookup()->first == ",") { 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()->first == "(") { ++nesting; arg->push_back(u); } else if (u.lookup()->first != ")") { 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 { make_token(placemarker, "") }; } } 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: if (i.lookup()->first == "#" || i.lookup()->first == "%:") { 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) \ /**/ std::cout << "#define O 0 +X ## Y A\n"; define("O", std::vector { T( (number, "0") (whitespace, " ") (op_or_punc, "+") (identifier, "X") (whitespace, " ") (op_or_punc | special, "##") (whitespace, " ") (identifier, "Y") (whitespace, " ") (identifier, "A") ) }); std::cout << "#define A() A() B\n"; define("A", 0, false, std::vector { T( (identifier, "A") (op_or_punc, "(") (op_or_punc, ")") (whitespace, " ") (identifier, "B") ) }); std::cout << "#define B() B() A\n"; define("B", 0, false, std::vector { T( (identifier, "B") (op_or_punc, "(") (op_or_punc, ")") (whitespace, " ") (identifier, "A") ) }); std::cout << "#define C(x, y) x ## y\n"; define("C", 2, false, std::vector { T( (formal | unscanned, "0") (whitespace, " ") (op_or_punc | special, "##") (whitespace, " ") (formal | unscanned, "1") ) }); std::cout << "#define D(...) D D ## __VA_ARGS__ __VA_ARGS__ #__VA_ARGS__\n"; define("D", 1, true, std::vector { T( (identifier, "D") (whitespace, " ") (identifier, "D") (whitespace, " ") (op_or_punc | special, "##") (whitespace, " ") (formal | unscanned, "0") (whitespace, " ") (formal | scanned, "0") (whitespace, " ") (formal | stringized, "0") ) }); std::cout << "#define ID(...) __VA_ARGS__\n"; define("ID", 1, true, std::vector { T( (formal | scanned, "0") ) }); std::cout << "#define P(p, x) p ## x(P\n"; define("P", 2, false, std::vector { T( (formal | unscanned, "0") (whitespace, " ") (op_or_punc | special, "##") (whitespace, " ") (formal | unscanned, "1") (op_or_punc, "(") (identifier, "P") ) }); std::cout.put('\n'); // ----- std::cout << "O()\n" << "A()()()\n" << "C(C,)\n" << "D(D(0, 1))\n" << "ID (\n" << "\t1\n" << ")\n" << "P(,ID)(1,2),P(1,2)))\n" << '\n'; // ----- std::vector input { T( (identifier, "O")(op_or_punc, "(")(op_or_punc, ")")(whitespace, "\n") (identifier, "A")(op_or_punc, "(")(op_or_punc, ")")(op_or_punc, "(")(op_or_punc, ")")(op_or_punc, "(")(op_or_punc, ")")(whitespace, "\n") (identifier, "C")(op_or_punc, "(")(identifier, "C")(op_or_punc, ",")(op_or_punc, ")")(whitespace, "\n") (identifier, "D")(op_or_punc, "(")(identifier, "D")(op_or_punc, "(")(number, "0")(op_or_punc, ",")(whitespace, " ")(number, "1")(op_or_punc, ")")(op_or_punc, ")")(whitespace, "\n") (identifier, "ID")(whitespace, " ")(whitespace, " ")(op_or_punc, "(")(whitespace, "\n")(whitespace, "\t")(number, "1")(whitespace, "\n")(op_or_punc, ")")(whitespace, "\n") (identifier, "P")(op_or_punc, "(")(op_or_punc, ",")(identifier, "ID")(op_or_punc, ")")(op_or_punc, "(")(number, "1")(op_or_punc, ",")(number, "2")(op_or_punc, ")")(op_or_punc, ",")(identifier, "P")(op_or_punc, "(")(number, "1")(op_or_punc, ",")(number, "2")(op_or_punc, ")")(op_or_punc, ")")(op_or_punc, ")")(whitespace, "\n") ) }; #undef T #undef T_I // ----- std::function sep = [] () -> const char* { return ""; }; for (const token& t : scanner::const_iterator>(input.begin(), input.end())) { // (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; }