Parse free functions

This commit is contained in:
Jonathan Müller 2017-03-11 18:08:06 +01:00
commit 9267bbbff2
15 changed files with 442 additions and 58 deletions

View file

@ -38,11 +38,11 @@ void detail::print_cursor_info(const CXCursor& cur) noexcept
cxstring(clang_getCursorKindSpelling(cur.kind)).c_str());
}
void detail::print_tokens(const detail::cxtranslation_unit& tu, const CXFile& file,
void detail::print_tokens(const CXTranslationUnit& tu, const CXFile& file,
const CXCursor& cur) noexcept
{
std::lock_guard<std::mutex> lock(mtx);
detail::tokenizer tokenizer(tu.get(), file, cur);
detail::tokenizer tokenizer(tu, file, cur);
for (auto& token : tokenizer)
std::printf("%s ", token.c_str());
std::puts("\n");

View file

@ -19,7 +19,7 @@ namespace cppast
void print_cursor_info(const CXCursor& cur) noexcept;
void print_tokens(const cxtranslation_unit& tu, const CXFile& file,
void print_tokens(const CXTranslationUnit& tu, const CXFile& file,
const CXCursor& cur) noexcept;
}
} // namespace cppast::detail

View file

@ -10,11 +10,11 @@ using namespace cppast;
namespace
{
std::string get_expression_str(detail::token_stream& stream)
std::string get_expression_str(detail::token_stream& stream, detail::token_iterator end)
{
// just concat everything
std::string expr;
while (!stream.done())
while (stream.cur() != end)
expr += stream.get().c_str();
return expr;
}
@ -30,7 +30,7 @@ std::unique_ptr<cpp_expression> detail::parse_expression(const detail::parse_con
detail::token_stream stream(tokenizer, cur);
auto type = parse_type(context, clang_getCursorType(cur));
auto expr = get_expression_str(stream);
auto expr = get_expression_str(stream, stream.end());
if (kind == CXCursor_CallExpr && (expr.empty() || expr.back() != ')'))
{
// we have a call expression that doesn't end in a closing parentheses
@ -47,12 +47,13 @@ std::unique_ptr<cpp_expression> detail::parse_expression(const detail::parse_con
return cpp_unexposed_expression::build(std::move(type), std::move(expr));
}
std::unique_ptr<cpp_expression> detail::parse_raw_expression(const parse_context& context,
token_stream& stream,
const CXType& type)
std::unique_ptr<cpp_expression> detail::parse_raw_expression(const parse_context&,
token_stream& stream,
token_iterator end,
std::unique_ptr<cpp_type> type)
{
if (stream.done())
return nullptr;
auto expr = get_expression_str(stream);
return cpp_unexposed_expression::build(parse_type(context, type), std::move(expr));
auto expr = get_expression_str(stream, end);
return cpp_unexposed_expression::build(std::move(type), std::move(expr));
}

View file

@ -0,0 +1,128 @@
// Copyright (C) 2017 Jonathan Müller <jonathanmueller.dev@gmail.com>
// This file is subject to the license terms in the LICENSE file
// found in the top-level directory of this distribution.
#include <cppast/cpp_function.hpp>
#include "libclang_visitor.hpp"
#include "parse_functions.hpp"
using namespace cppast;
namespace
{
std::unique_ptr<cpp_function_parameter> parse_parameter(const detail::parse_context& context,
const CXCursor& cur)
{
auto name = detail::get_cursor_name(cur);
auto type = detail::parse_type(context, clang_getCursorType(cur));
std::unique_ptr<cpp_expression> default_value;
detail::visit_children(cur, [&](const CXCursor& child) {
DEBUG_ASSERT(clang_isExpression(child.kind) && !default_value,
detail::parse_error_handler{}, child,
"unexpected child cursor of function parameter");
default_value = detail::parse_expression(context, child);
});
return cpp_function_parameter::build(*context.idx, detail::get_entity_id(cur), name.c_str(),
std::move(type), std::move(default_value));
}
template <class Builder>
void add_parameters(const detail::parse_context& context, Builder& builder, const CXCursor& cur)
{
detail::visit_children(cur, [&](const CXCursor& child) {
if (clang_getCursorKind(child) != CXCursor_ParmDecl)
return;
try
{
auto parameter = parse_parameter(context, child);
builder.add_parameter(std::move(parameter));
}
catch (detail::parse_error& ex)
{
context.logger->log("libclang parser", ex.get_diagnostic());
}
});
}
void skip_parameters(detail::token_stream& stream)
{
detail::skip_brackets(stream);
}
std::unique_ptr<cpp_expression> try_parse_noexcept(detail::token_stream& stream,
const detail::parse_context& context)
{
if (!detail::skip_if(stream, "noexcept"))
return nullptr;
auto type = cpp_builtin_type::build("bool");
if (stream.peek().value() != "(")
return cpp_literal_expression::build(std::move(type), "true");
auto closing = detail::find_closing_bracket(stream);
detail::skip(stream, "(");
auto expr = detail::parse_raw_expression(context, stream, closing, std::move(type));
detail::skip(stream, ")");
return expr;
}
cpp_function_body_kind parse_body_kind(detail::token_stream& stream)
{
if (detail::skip_if(stream, "default"))
return cpp_function_defaulted;
else if (detail::skip_if(stream, "delete"))
return cpp_function_deleted;
DEBUG_UNREACHABLE(detail::parse_error_handler{}, stream.cursor(),
"unexpected token for function body kind");
return cpp_function_declaration;
}
}
std::unique_ptr<cpp_entity> detail::parse_cpp_function(const detail::parse_context& context,
const CXCursor& cur)
{
DEBUG_ASSERT(clang_getCursorKind(cur) == CXCursor_FunctionDecl, detail::assert_handler{});
auto name = detail::get_cursor_name(cur);
cpp_function::builder builder(name.c_str(),
detail::parse_type(context, clang_getCursorResultType(cur)));
add_parameters(context, builder, cur);
if (clang_Cursor_isVariadic(cur))
builder.is_variadic();
builder.storage_class(detail::get_storage_class(cur));
detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, cur);
// parse prefix
while (!detail::skip_if(stream, name.c_str()))
{
if (detail::skip_if(stream, "constexpr"))
builder.is_constexpr();
else
stream.bump();
}
// skip parameters
skip_parameters(stream);
auto body = clang_isCursorDefinition(cur) ? cpp_function_definition : cpp_function_declaration;
// parse suffix
// tokenizer only tokenizes signature, so !stream.done() is sufficient
while (!stream.done())
{
if (auto expr = try_parse_noexcept(stream, context))
builder.noexcept_condition(std::move(expr));
else if (skip_if(stream, "="))
body = parse_body_kind(stream);
else
stream.bump();
}
return builder.finish(*context.idx, detail::get_entity_id(cur), body);
}

View file

@ -156,7 +156,7 @@ namespace
}
}
return tu;
return detail::cxtranslation_unit(tu);
}
unsigned get_line_no(const CXCursor& cursor)

View file

@ -4,6 +4,8 @@
#include "parse_functions.hpp"
#include <cppast/cpp_storage_class_specifiers.hpp>
using namespace cppast;
cpp_entity_id detail::get_entity_id(const CXCursor& cur)
@ -18,6 +20,35 @@ detail::cxstring detail::get_cursor_name(const CXCursor& cur)
return cxstring(clang_getCursorSpelling(cur));
}
cpp_storage_class_specifiers detail::get_storage_class(const CXCursor& cur)
{
switch (clang_Cursor_getStorageClass(cur))
{
case CX_SC_Invalid:
break;
case CX_SC_None:
return cpp_storage_class_none;
case CX_SC_Auto:
case CX_SC_Register:
return cpp_storage_class_auto;
case CX_SC_Extern:
return cpp_storage_class_extern;
case CX_SC_Static:
return cpp_storage_class_static;
case CX_SC_PrivateExtern:
case CX_SC_OpenCLWorkGroupLocal:
// non-exposed storage classes
return cpp_storage_class_auto;
}
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected storage class");
return cpp_storage_class_auto;
}
std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& context,
const CXCursor& cur) try
{
@ -54,6 +85,9 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
case CXCursor_FieldDecl:
return parse_cpp_member_variable(context, cur);
case CXCursor_FunctionDecl:
return parse_cpp_function(context, cur);
default:
break;
}

View file

@ -16,6 +16,7 @@ namespace cppast
{
class cpp_expression;
class cpp_type;
enum cpp_storage_class_specifiers : int;
namespace detail
{
@ -26,6 +27,9 @@ namespace cppast
// as then you won't get it "as-is"
cxstring get_cursor_name(const CXCursor& cur);
// note: does not handle thread_local
cpp_storage_class_specifiers get_storage_class(const CXCursor& cur);
struct parse_context
{
CXTranslationUnit tu;
@ -39,12 +43,13 @@ namespace cppast
std::unique_ptr<cpp_expression> parse_expression(const parse_context& context,
const CXCursor& cur);
// parse the expression starting at the current token in the stream
// and ends at the end of the stream
// and ends at the given iterator
// this is required for situations where there is no expression cursor exposed,
// like member initializers
std::unique_ptr<cpp_expression> parse_raw_expression(const parse_context& context,
token_stream& stream,
const CXType& type);
std::unique_ptr<cpp_expression> parse_raw_expression(const parse_context& context,
token_stream& stream,
token_iterator end,
std::unique_ptr<cpp_type> type);
// parse_entity() dispatches on the cursor type
// it calls one of the other parse functions defined elsewhere
@ -76,6 +81,9 @@ namespace cppast
std::unique_ptr<cpp_entity> parse_cpp_member_variable(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_function(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_entity(const parse_context& context, const CXCursor& cur);
}
} // namespace cppast::detail

View file

@ -28,7 +28,7 @@ namespace cppast
{
}
raii_wrapper(T obj) noexcept : obj_(obj)
explicit raii_wrapper(T obj) noexcept : obj_(obj)
{
DEBUG_ASSERT(obj_, detail::assert_handler{});
}

View file

@ -124,11 +124,21 @@ namespace cppast
return cursor_;
}
token_iterator begin() const noexcept
{
return begin_;
}
token_iterator cur() const noexcept
{
return cur_;
}
token_iterator end() const noexcept
{
return end_;
}
void set_cur(token_iterator iter) noexcept
{
cur_ = iter;

View file

@ -183,6 +183,9 @@ namespace
return cpp_ref_none;
}
std::unique_ptr<cpp_type> parse_type_impl(const detail::parse_context& context,
const CXType& type);
std::unique_ptr<cpp_expression> parse_array_size(const CXType& type)
{
auto size = clang_getArraySize(type);
@ -214,8 +217,27 @@ namespace
size_expr.rend()));
}
std::unique_ptr<cpp_type> parse_type_impl(const detail::parse_context& context,
const CXType& type);
std::unique_ptr<cpp_type> try_parse_array_type(const detail::parse_context& context,
const CXType& type)
{
auto canonical = clang_getCanonicalType(type);
auto value_type = clang_getArrayElementType(type);
if (value_type.kind == CXType_Invalid)
{
// value_type is invalid, however type can still be an array
// as there seems to be a libclang bug
// only if the canonical type is not an array,
// is it truly not an array
value_type = clang_getArrayElementType(canonical);
if (value_type.kind == CXType_Invalid)
return nullptr;
// we have an array, even though type isn't one directly
// only downside of this workaround: we've stripped away typedefs
}
auto size = parse_array_size(canonical); // type may not work, see above
return cpp_array_type::build(parse_type_impl(context, value_type), std::move(size));
}
template <class Builder>
std::unique_ptr<cpp_type> add_parameters(Builder& builder, const detail::parse_context& context,
@ -317,6 +339,9 @@ namespace
// guess what: after you've called clang_getPointeeType() on a function pointer
// you'll get an unexposed type
return ftype;
else if (auto atype = try_parse_array_type(context, type))
// same deal here
return atype;
return cpp_unexposed_type::build(get_type_spelling(type).c_str());
case CXType_Void:
@ -379,11 +404,7 @@ namespace
case CXType_VariableArray:
case CXType_DependentSizedArray:
case CXType_ConstantArray:
{
auto size = parse_array_size(type);
auto value_type = parse_type_impl(context, clang_getArrayElementType(type));
return cpp_array_type::build(std::move(value_type), std::move(size));
}
return try_parse_array_type(context, type);
case CXType_FunctionNoProto:
case CXType_FunctionProto:

View file

@ -28,35 +28,6 @@ namespace
});
return expression;
}
cpp_storage_class_specifiers parse_storage_class(const CXCursor& cur)
{
switch (clang_Cursor_getStorageClass(cur))
{
case CX_SC_Invalid:
break;
case CX_SC_None:
return cpp_storage_class_none;
case CX_SC_Auto:
case CX_SC_Register:
return cpp_storage_class_auto;
case CX_SC_Extern:
return cpp_storage_class_extern;
case CX_SC_Static:
return cpp_storage_class_static;
case CX_SC_PrivateExtern:
case CX_SC_OpenCLWorkGroupLocal:
// non-exposed storage classes
return cpp_storage_class_auto;
}
DEBUG_UNREACHABLE(detail::parse_error_handler{}, cur, "unexpected storage class");
return cpp_storage_class_auto;
}
}
std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_context& context,
@ -66,7 +37,7 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_variable(const detail::parse_conte
auto name = get_cursor_name(cur);
auto type = parse_type(context, clang_getCursorType(cur));
auto storage_class = parse_storage_class(cur);
auto storage_class = get_storage_class(cur);
auto is_constexpr = false;
// just look for thread local or constexpr
@ -118,7 +89,8 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_member_variable(const detail::pars
// look for the equal sign, default value starts there
while (!stream.done() && !skip_if(stream, "="))
stream.bump();
auto default_value = parse_raw_expression(context, stream, clang_getCursorType(cur));
auto default_value = parse_raw_expression(context, stream, stream.end(),
parse_type(context, clang_getCursorType(cur)));
return cpp_member_variable::build(*context.idx, get_entity_id(cur), name.c_str(),
std::move(type), std::move(default_value), is_mutable);