diff --git a/src/libclang/parse_error.hpp b/src/libclang/parse_error.hpp index 8398360..6dddc33 100644 --- a/src/libclang/parse_error.hpp +++ b/src/libclang/parse_error.hpp @@ -6,6 +6,7 @@ #define CPPAST_PARSE_ERROR_HPP_INCLUDED #include +#include #include #include @@ -45,11 +46,20 @@ namespace cppast struct parse_error_handler : debug_assert::set_level<1>, debug_assert::allow_exception { static void handle(const debug_assert::source_location&, const char*, - const CXCursor& cur, const char* message) + const CXCursor& cur, std::string message) { - throw parse_error(cur, message); + throw parse_error(cur, std::move(message)); } }; + + template + std::string format(Args&&... args) + { + std::ostringstream stream; + int dummy[] = {(stream << std::forward(args), 0)...}; + (void)dummy; + return stream.str(); + } } } // namespace cppast::detail diff --git a/src/libclang/tokenizer.cpp b/src/libclang/tokenizer.cpp index 43830a4..4c52ae0 100644 --- a/src/libclang/tokenizer.cpp +++ b/src/libclang/tokenizer.cpp @@ -167,3 +167,102 @@ detail::tokenizer::tokenizer(const detail::cxtranslation_unit& tu, const CXFile& for (auto i = 0u; i != tokenizer.size(); ++i) tokens_.emplace_back(tu, tokenizer[i]); } + +void detail::skip(detail::token_stream& stream, const char* str) +{ + auto& token = stream.peek(); + DEBUG_ASSERT(token == str, parse_error_handler{}, stream.cursor(), + format("expected '", str, "', got '", token.c_str(), "'")); + stream.bump(); +} + +bool detail::skip_if(detail::token_stream& stream, const char* str) +{ + auto& token = stream.peek(); + if (token != str) + return false; + stream.bump(); + return true; +} + +detail::token_iterator detail::find_closing_bracket(detail::token_stream stream) +{ + auto template_bracket = false; + auto open_bracket = stream.peek().c_str(); + const char* close_bracket = nullptr; + if (skip_if(stream, "(")) + close_bracket = ")"; + else if (skip_if(stream, "{")) + close_bracket = "}"; + else if (skip_if(stream, "[")) + close_bracket = "]"; + else if (skip_if(stream, "<")) + { + close_bracket = "<"; + template_bracket = true; + } + else + DEBUG_UNREACHABLE(parse_error_handler{}, stream.cursor(), + format("expected a bracket, got '", stream.peek().c_str(), "'")); + + auto bracket_count = 1; + auto paren_count = 0; // internal nested parenthesis + while (bracket_count != 0) + { + auto& cur = stream.get().value(); + if (paren_count == 0 && cur == open_bracket) + ++bracket_count; + else if (paren_count == 0 && cur == close_bracket) + --bracket_count; + else if (paren_count == 0 && template_bracket && cur == ">>") + // maximal munch + bracket_count -= 2u; + else if (cur == "(") + ++paren_count; + else if (cur == ")") + --paren_count; + } + stream.bump_back(); + DEBUG_ASSERT(paren_count == 0 && stream.peek().value() == close_bracket, parse_error_handler{}, + stream.cursor(), "find_closing_bracket() internal parse error"); + return stream.cur(); +} + +void detail::skip_brackets(detail::token_stream& stream) +{ + auto closing = find_closing_bracket(stream); + stream.set_cur(std::next(closing)); +} + +bool detail::skip_attribute(detail::token_stream& stream) +{ + if (skip_if(stream, "[") && stream.peek() == "[") + { + // C++11 attribute + // [[]] + // ^ + skip_brackets(stream); + // [[]] + // ^ + skip(stream, "]"); + return true; + } + else if (skip_if(stream, "__attribute__")) + { + // GCC/clang attributes + // __attribute__() + // ^ + skip_brackets(stream); + return true; + } + else if (skip_if(stream, "__declspec")) + { + // MSVC declspec + // __declspec() + // ^ + skip_brackets(stream); + return true; + } + + return false; +} diff --git a/src/libclang/tokenizer.hpp b/src/libclang/tokenizer.hpp index 08e4906..d5a9c14 100644 --- a/src/libclang/tokenizer.hpp +++ b/src/libclang/tokenizer.hpp @@ -38,18 +38,40 @@ namespace cppast CXTokenKind kind_; }; + inline bool operator==(const token& tok, const char* str) noexcept + { + return tok.value() == str; + } + + inline bool operator==(const char* str, const token& tok) noexcept + { + return str == tok.value(); + } + + inline bool operator!=(const token& tok, const char* str) noexcept + { + return !(tok == str); + } + + inline bool operator!=(const char* str, const token& tok) noexcept + { + return !(str == tok); + } + + using token_iterator = std::vector::const_iterator; + class tokenizer { public: explicit tokenizer(const cxtranslation_unit& tu, const CXFile& file, const CXCursor& cur); - std::vector::const_iterator begin() const noexcept + token_iterator begin() const noexcept { return tokens_.begin(); } - std::vector::const_iterator end() const noexcept + token_iterator end() const noexcept { return tokens_.end(); } @@ -57,6 +79,85 @@ namespace cppast private: std::vector tokens_; }; + + class token_stream + { + public: + explicit token_stream(const tokenizer& tokenizer, const CXCursor& cur) + : cursor_(cur), begin_(tokenizer.begin()), cur_(begin_), end_(tokenizer.end()) + { + } + + const token& peek() const noexcept + { + if (done()) + return *std::prev(end_); + return *cur_; + } + + void bump() noexcept + { + if (cur_ != end_) + ++cur_; + } + + void bump_back() noexcept + { + if (cur_ != begin_) + --cur_; + } + + const token& get() noexcept + { + auto& result = peek(); + bump(); + return result; + } + + bool done() const noexcept + { + return cur_ == end_; + } + + const CXCursor& cursor() const noexcept + { + return cursor_; + } + + token_iterator cur() const noexcept + { + return cur_; + } + + void set_cur(token_iterator iter) noexcept + { + cur_ = iter; + } + + private: + CXCursor cursor_; + token_iterator begin_, cur_, end_; + }; + + // skips the next token + // asserts that it has the given string + void skip(token_stream& stream, const char* str); + + // skips the next token if it has the given string + bool skip_if(token_stream& stream, const char* str); + + // returns the location of the closing bracket + // the current token must be (,[,{ or < + // note: < might not work in the arguments of a template specialization + token_iterator find_closing_bracket(token_stream stream); + + // skips brackets + // the current token must be (,[,{ or < + // note: < might not work in the arguments of a template specialization + void skip_brackets(token_stream& stream); + + // skips an attribute + bool skip_attribute(token_stream& stream); } } // namespace cppast::detail