Parse enums
This commit is contained in:
parent
0794ff1420
commit
a47e66e2c4
16 changed files with 262 additions and 109 deletions
BIN
C++ Types.xmind
BIN
C++ Types.xmind
Binary file not shown.
|
|
@ -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)
|
||||
6
TODO.md
6
TODO.md
|
|
@ -1,6 +0,0 @@
|
|||
Milestone 0 - Hierachy
|
||||
|
||||
* overloaded entity ref
|
||||
* namespace id stuff
|
||||
* preprocessor
|
||||
* translation_unit
|
||||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
77
main.cpp
77
main.cpp
|
|
@ -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;
|
||||
});
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
|
|||
82
src/libclang/enum_parser.cpp
Normal file
82
src/libclang/enum_parser.cpp
Normal 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));
|
||||
}
|
||||
34
src/libclang/expression_parser.cpp
Normal file
34
src/libclang/expression_parser.cpp
Normal 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));
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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));
|
||||
}
|
||||
|
|
|
|||
0
test.cpp
0
test.cpp
112
test/cpp_enum.cpp
Normal file
112
test/cpp_enum.cpp
Normal 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);
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue