Parse enums

This commit is contained in:
Jonathan Müller 2017-02-23 17:52:11 +01:00
commit a47e66e2c4
16 changed files with 262 additions and 109 deletions

Binary file not shown.

View file

@ -1,11 +0,0 @@
cmake_minimum_required(VERSION 3.6)
project(cppast)
set(CMAKE_CXX_STANDARD 11)
set(SOURCE_FILES include/cppast/cpp_entity.hpp include/cppast/cpp_entity_kind.hpp include/cppast/detail/intrusive_list.hpp include/cppast/detail/assert.hpp include/cppast/cpp_entity_container.hpp src/cpp_entity_kind.cpp include/cppast/cpp_file.hpp src/cpp_file.cpp include/cppast/cpp_namespace.hpp include/cppast/cpp_entity_index.hpp include/cppast/cpp_entity_ref.hpp include/cppast/cpp_type.hpp src/cpp_namespace.cpp src/cpp_type.cpp include/cppast/cpp_expression.hpp include/cppast/cpp_array_type.hpp include/cppast/cpp_function_type.hpp include/cppast/cpp_enum.hpp src/cpp_enum.cpp src/cpp_entity.cpp include/cppast/visitor.hpp src/visitor.cpp src/cpp_type_alias.cpp include/cppast/cpp_type_alias.hpp include/cppast/cpp_variable_base.hpp src/cpp_variable.cpp include/cppast/cpp_variable.hpp src/cpp_language_linkage.cpp include/cppast/cpp_language_linkage.hpp src/cpp_function.cpp include/cppast/cpp_function.hpp include/cppast/cpp_storage_specifiers.hpp src/cpp_class.cpp include/cppast/cpp_class.hpp src/cpp_member_variable.cpp include/cppast/cpp_member_variable.hpp src/cpp_member_function.cpp include/cppast/cpp_member_function.hpp src/cpp_template_parameter.cpp include/cppast/cpp_template_parameter.hpp include/cppast/cpp_alias_template.hpp src/cpp_alias_template.cpp include/cppast/cpp_template.hpp src/cpp_function_template.cpp include/cppast/cpp_function_template.hpp src/cpp_class_template.cpp include/cppast/cpp_class_template.hpp src/cpp_variable_template.cpp include/cppast/cpp_variable_template.hpp)
add_library(cppast ${SOURCE_FILES})
target_include_directories(cppast PUBLIC ../type_safe/include ../debug_assert include/)
add_executable(cppast_main main.cpp)
target_link_libraries(cppast_main PUBLIC cppast)

View file

@ -1,6 +0,0 @@
Milestone 0 - Hierachy
* overloaded entity ref
* namespace id stuff
* preprocessor
* translation_unit

View file

@ -21,6 +21,8 @@ namespace cppast
class cpp_enum_value final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;
/// \returns A newly created and registered enum value.
/// \notes `value` may be `nullptr`, in which case the enum has an implicit value.
static std::unique_ptr<cpp_enum_value> build(
@ -49,6 +51,8 @@ namespace cppast
class cpp_enum final : public cpp_entity, public cpp_entity_container<cpp_enum, cpp_enum_value>
{
public:
static cpp_entity_kind kind() noexcept;
/// Builds a [cppast::cpp_enum]().
class builder
{

View file

@ -1,77 +0,0 @@
// 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 <cassert>
#include <iostream>
#include <cppast/cpp_array_type.hpp>
#include <cppast/cpp_class.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_enum.hpp>
#include <cppast/cpp_file.hpp>
#include <cppast/cpp_function_type.hpp>
#include <cppast/cpp_function.hpp>
#include <cppast/cpp_namespace.hpp>
#include <cppast/cpp_variable.hpp>
#include <cppast/cpp_alias_template.hpp>
#include <cppast/visitor.hpp>
std::unique_ptr<cppast::cpp_file> make_file(const cppast::cpp_entity_index& idx)
{
using namespace cppast;
cpp_file::builder file_builder("main.cpp");
file_builder.add_child(cpp_namespace::builder("foo", false).finish(idx, "foo"_id));
cpp_namespace::builder ns_builder("bar", false);
ns_builder.add_child(cpp_namespace::builder("foo", false).finish(idx, "bar::foo"_id));
file_builder.add_child(ns_builder.finish(idx, "bar"_id));
cpp_enum::builder e_builder("e", true);
e_builder.add_value(cpp_enum_value::build(idx, "e::a"_id, "a", nullptr));
file_builder.add_child(e_builder.finish(idx, "e"_id));
cpp_function::builder f_builder("func", cpp_builtin_type::build("int"));
f_builder.add_parameter(
cpp_function_parameter::build(idx, "param"_id, "param", cpp_builtin_type::build("int")));
f_builder.is_variadic();
file_builder.add_child(f_builder.finish(idx, "func"_id));
cpp_class::builder c_builder("type", cpp_class_kind::class_t, false);
c_builder.access_specifier(cpp_public);
c_builder.add_child(cpp_variable::build(idx, "var"_id, "var", cpp_builtin_type::build("int"),
nullptr, cpp_storage_class_none, false));
file_builder.add_child(c_builder.finish(idx, "type"_id));
cpp_alias_template::builder a_builder(
cpp_type_alias::build("alias", cpp_template_parameter_type::build(
cpp_template_type_parameter_ref("T"_id, "T"))));
a_builder.add_parameter(
cpp_template_type_parameter::build(idx, "T"_id, "T", cpp_template_keyword::keyword_typename,
false, nullptr));
file_builder.add_child(a_builder.finish(idx, "alias"_id));
return file_builder.finish(idx);
}
int main()
{
cppast::cpp_entity_index idx;
auto file = make_file(idx);
auto level = 0;
cppast::visit(*file, [&](const cppast::cpp_entity& e, cppast::visitor_info info) {
if (info == cppast::visitor_info::container_entity_exit)
--level;
else
{
std::cout << std::string(level, ' ') << cppast::to_string(e.kind()) << ' ' << e.name()
<< ' ' << cppast::full_name(e) << '\n';
if (info == cppast::visitor_info::container_entity_enter)
++level;
}
return true;
});
}

View file

@ -8,6 +8,11 @@
using namespace cppast;
cpp_entity_kind cpp_enum_value::kind() noexcept
{
return cpp_entity_kind::enum_value_t;
}
std::unique_ptr<cpp_enum_value> cpp_enum_value::build(const cpp_entity_index& idx, cpp_entity_id id,
std::string name,
std::unique_ptr<cpp_expression> value)
@ -20,12 +25,17 @@ std::unique_ptr<cpp_enum_value> cpp_enum_value::build(const cpp_entity_index& id
cpp_entity_kind cpp_enum_value::do_get_entity_kind() const noexcept
{
return cpp_entity_kind::enum_value_t;
return kind();
}
cpp_entity_kind cpp_enum::kind() noexcept
{
return cpp_entity_kind::enum_t;
}
cpp_entity_kind cpp_enum::do_get_entity_kind() const noexcept
{
return cpp_entity_kind::enum_t;
return kind();
}
type_safe::optional<std::string> cpp_enum::do_get_scope_name() const

View file

@ -0,0 +1,82 @@
// 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_enum.hpp>
#include "parse_functions.hpp"
#include "libclang_visitor.hpp"
using namespace cppast;
namespace
{
std::unique_ptr<cpp_enum_value> parse_enum_value(const detail::parse_context& context,
const CXCursor& cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_EnumConstantDecl, detail::parse_error_handler{}, cur,
"unexpected child cursor of enum");
detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, cur);
// <identifier>,
// or: <identifier> = <expression>,
auto& name = stream.get().value();
std::unique_ptr<cpp_expression> value;
if (detail::skip_if(stream, "="))
{
detail::visit_children(cur, [&](const CXCursor& child) {
DEBUG_ASSERT(clang_isExpression(child.kind) && !value,
detail::parse_error_handler{}, cur,
"unexpected child cursor of enum value");
value = detail::parse_expression(context, child);
});
}
return cpp_enum_value::build(*context.idx, detail::get_entity_id(cur), name.c_str(),
std::move(value));
}
cpp_enum::builder make_enum_builder(const detail::parse_context& context, const CXCursor& cur)
{
detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, cur);
// enum [class] name [: type] {
detail::skip(stream, "enum");
auto scoped = detail::skip_if(stream, "class");
auto& name = stream.get().value();
std::unique_ptr<cpp_type> type;
if (detail::skip_if(stream, ":"))
// parse type, explictly given one
type = detail::parse_type(context, clang_getEnumDeclIntegerType(cur));
return cpp_enum::builder(name.c_str(), scoped, std::move(type));
}
}
std::unique_ptr<cpp_entity> detail::parse_cpp_enum(const detail::parse_context& context,
const CXCursor& cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_EnumDecl, detail::assert_handler{});
if (!clang_isCursorDefinition(cur))
return nullptr;
auto builder = make_enum_builder(context, cur);
detail::visit_children(cur, [&](const CXCursor& child) {
try
{
auto entity = parse_enum_value(context, child);
builder.add_value(std::move(entity));
}
catch (parse_error& ex)
{
context.logger->log("libclang parser", ex.get_diagnostic());
}
});
return builder.finish(*context.idx, get_entity_id(cur));
}

View file

@ -0,0 +1,34 @@
// 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_expression.hpp>
#include "parse_functions.hpp"
using namespace cppast;
std::unique_ptr<cpp_expression> detail::parse_expression(const detail::parse_context& context,
const CXCursor& cur)
{
auto kind = clang_getCursorKind(cur);
DEBUG_ASSERT(clang_isExpression(kind), detail::assert_handler{});
detail::tokenizer tokenizer(context.tu, context.file, cur);
detail::token_stream stream(tokenizer, cur);
// just concat everything
std::string expr;
while (!stream.done())
expr += stream.get().c_str();
auto type = parse_type(context, clang_getCursorType(cur));
if (kind == CXCursor_CharacterLiteral || kind == CXCursor_CompoundLiteralExpr
|| kind == CXCursor_FloatingLiteral || kind == CXCursor_ImaginaryLiteral
|| kind == CXCursor_IntegerLiteral || kind == CXCursor_StringLiteral
|| kind == CXCursor_CXXBoolLiteralExpr || kind == CXCursor_CXXNullPtrLiteralExpr)
return cpp_literal_expression::build(std::move(type), std::move(expr));
else
return cpp_unexposed_expression::build(std::move(type), std::move(expr));
}

View file

@ -38,6 +38,9 @@ std::unique_ptr<cpp_entity> detail::parse_entity(const detail::parse_context& co
case CXCursor_TypedefDecl:
return parse_cpp_type_alias(context, cur);
case CXCursor_EnumDecl:
return parse_cpp_enum(context, cur);
default:
break;
}

View file

@ -14,6 +14,7 @@
namespace cppast
{
class cpp_expression;
class cpp_type;
namespace detail
@ -30,6 +31,9 @@ namespace cppast
std::unique_ptr<cpp_type> parse_type(const parse_context& context, const CXType& type);
std::unique_ptr<cpp_expression> parse_expression(const parse_context& context,
const CXCursor& cur);
// parse_entity() dispatches on the cursor type
// it calls one of the other parse functions defined elsewhere
// try_parse_XXX are not exposed entities
@ -50,6 +54,9 @@ namespace cppast
std::unique_ptr<cpp_entity> parse_cpp_type_alias(const parse_context& context,
const CXCursor& cur);
std::unique_ptr<cpp_entity> parse_cpp_enum(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

@ -152,6 +152,9 @@ namespace
} while (!token_after_is(tu, file, cur, end, ";"));
end = get_next_location(tu, file, end);
}
else if (clang_isExpression(clang_getCursorKind(cur)))
// need to shrink range by one
end = get_next_location(tu, file, end, -1);
return clang_getRange(begin, end);
}

View file

@ -399,17 +399,12 @@ namespace
}
std::unique_ptr<cpp_type> detail::parse_type(const detail::parse_context& context,
const CXType& type) try
const CXType& type)
{
auto result = parse_type_impl(context, type);
DEBUG_ASSERT(result && is_valid(*result), detail::parse_error_handler{}, type, "invalid type");
return std::move(result);
}
catch (parse_error& ex)
{
context.logger->log("libclang parser", ex.get_diagnostic());
return nullptr;
}
std::unique_ptr<cpp_entity> detail::parse_cpp_type_alias(const detail::parse_context& context,
const CXCursor& cur)
@ -419,7 +414,5 @@ std::unique_ptr<cpp_entity> detail::parse_cpp_type_alias(const detail::parse_con
auto name = cxstring(clang_getCursorSpelling(cur));
auto type = parse_type(context, clang_getTypedefDeclUnderlyingType(cur));
if (!type)
return nullptr;
return cpp_type_alias::build(*context.idx, get_entity_id(cur), name.c_str(), std::move(type));
}

View file

112
test/cpp_enum.cpp Normal file
View file

@ -0,0 +1,112 @@
// 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_enum.hpp>
#include "test_parser.hpp"
using namespace cppast;
TEST_CASE("cpp_enum")
{
auto code = R"(
enum ignore_me : int;
enum a
{
a_a,
a_b = 42,
a_c,
a_d = a_a + 2,
};
enum class ignore_me2;
enum class b : int
{
b_a,
b_b = 42,
b_c
};
)";
cpp_entity_index idx;
auto file = parse(idx, "cpp_enum.cpp", code);
auto count = test_visit<cpp_enum>(*file, [&](const cpp_enum& e) {
if (e.name() == "a")
{
REQUIRE(!e.is_scoped());
REQUIRE(!e.underlying_type());
auto no_vals = 0u;
for (auto& val : e)
{
if (val.name() == "a_a" || val.name() == "a_c")
{
++no_vals;
REQUIRE(!val.value());
}
else if (val.name() == "a_b")
{
++no_vals;
REQUIRE(val.value());
auto& expr = val.value().value();
REQUIRE(expr.kind() == cpp_expression_kind::unexposed);
REQUIRE(static_cast<const cpp_unexposed_expression&>(expr).expression()
== "42");
REQUIRE(
equal_types(idx, expr.type(), *cpp_builtin_type::build("unsigned int")));
}
else if (val.name() == "a_d")
{
++no_vals;
REQUIRE(val.value());
auto& expr = val.value().value();
// this is unexposed for some reason
REQUIRE(expr.kind() == cpp_expression_kind::unexposed);
REQUIRE(static_cast<const cpp_unexposed_expression&>(expr).expression()
== "a_a+2");
REQUIRE(
equal_types(idx, expr.type(), *cpp_builtin_type::build("unsigned int")));
}
else
REQUIRE(false);
}
REQUIRE(no_vals == 4u);
}
else if (e.name() == "b")
{
REQUIRE(e.is_scoped());
REQUIRE(e.underlying_type());
REQUIRE(equal_types(idx, e.underlying_type().value(), *cpp_builtin_type::build("int")));
auto no_vals = 0u;
for (auto& val : e)
{
REQUIRE(full_name(val) == "b::" + val.name());
if (val.name() == "b_a" || val.name() == "b_c")
{
++no_vals;
REQUIRE(!val.value());
}
else if (val.name() == "b_b")
{
++no_vals;
REQUIRE(val.value());
auto& expr = val.value().value();
REQUIRE(expr.kind() == cpp_expression_kind::literal);
REQUIRE(static_cast<const cpp_literal_expression&>(expr).value() == "42");
REQUIRE(equal_types(idx, expr.type(), *cpp_builtin_type::build("int")));
}
else
REQUIRE(false);
}
REQUIRE(no_vals == 3u);
}
else
REQUIRE(false);
});
REQUIRE(count == 2u);
}

View file

@ -1,5 +0,0 @@
// 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.
#i

View file

@ -10,6 +10,7 @@
#include <catch.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_type.hpp>
#include <cppast/libclang_parser.hpp>
#include <cppast/visitor.hpp>
@ -71,4 +72,7 @@ inline void check_parent(const cppast::cpp_entity& e, const char* parent_name,
REQUIRE(cppast::full_name(e) == full_name);
}
bool equal_types(const cppast::cpp_entity_index& idx, const cppast::cpp_type& parsed,
const cppast::cpp_type& synthesized);
#endif // CPPAST_TEST_PARSER_HPP_INCLUDED