Skip to content

Commit

Permalink
Add basic concept support (#144)
Browse files Browse the repository at this point in the history
  • Loading branch information
waitingtocompile committed Sep 3, 2022
1 parent f3e3995 commit a937efb
Show file tree
Hide file tree
Showing 21 changed files with 396 additions and 16 deletions.
74 changes: 74 additions & 0 deletions include/cppast/cpp_concept.hpp
@@ -0,0 +1,74 @@
// Copyright (C) 2017-2022 Jonathan Müller and cppast contributors
// SPDX-License-Identifier: MIT

#ifndef CPPAST_CPP_CONCEPT_HPP_INCLUDED
#define CPPAST_CPP_CONCEPT_HPP_INCLUDED

#include <cppast/cpp_entity.hpp>
#include <cppast/cpp_template_parameter.hpp>
#include <cppast/cpp_expression.hpp>

namespace cppast
{
/// A [[cppast::cpp_entity]() modelling a c++ concept declaration
/// \notes while concepts are technically templates,
/// this is not a [cppast::cpp_template](),
/// as concepts act very differently from other templates
class cpp_concept final : public cpp_entity
{
public:
static cpp_entity_kind kind() noexcept;

/// \returns the template parameters as a string
const cpp_token_string& parameters() const noexcept
{
return parameters_;
}

/// \returns the [cppast::cpp_expression]() defining the concept constraint
const cpp_expression& constraint_expression() const noexcept
{
return *expression_;
}

class builder
{
public:
builder(std::string name)
: concept_(new cpp_concept(std::move(name)))
{}

cpp_token_string& set_parameters(cpp_token_string string) noexcept
{
concept_->parameters_ = std::move(string);
return concept_->parameters_;
}

cpp_expression& set_expression(std::unique_ptr<cpp_expression> expression) noexcept
{
concept_->expression_ = std::move(expression);
return *concept_->expression_;
}

std::unique_ptr<cpp_concept> finish(const cpp_entity_index& idx, cpp_entity_id id);

private:
std::unique_ptr<cpp_concept> concept_;
};

private:
cpp_concept(std::string name)
: cpp_entity(std::move(name)), parameters_({})
{}

cpp_entity_kind do_get_entity_kind() const noexcept override;

cpp_token_string parameters_;

std::unique_ptr<cpp_expression> expression_;
};


} // namespace cppast

#endif
1 change: 1 addition & 0 deletions include/cppast/cpp_entity_kind.hpp
Expand Up @@ -57,6 +57,7 @@ enum class cpp_entity_kind
function_template_specialization_t,
class_template_t,
class_template_specialization_t,
concept_t,

static_assert_t,

Expand Down
2 changes: 1 addition & 1 deletion include/cppast/cpp_template.hpp
Expand Up @@ -15,7 +15,7 @@

namespace cppast
{
/// Base class for all entities modelling a C++ template of some kind.
/// Base class for all entities modelling a C++ template of some kind, aside from concepts
///
/// It is a container of a single [cppast::cpp_entity]() that is the entity being templated.
class cpp_template : public cpp_entity, public cpp_entity_container<cpp_template, cpp_entity>
Expand Down
17 changes: 13 additions & 4 deletions include/cppast/cpp_template_parameter.hpp
Expand Up @@ -36,7 +36,8 @@ class cpp_template_parameter : public cpp_entity
enum class cpp_template_keyword
{
keyword_class,
keyword_typename
keyword_typename,
concept_contraint
};

/// \returns The string associated of the keyword.
Expand All @@ -52,7 +53,8 @@ class cpp_template_type_parameter final : public cpp_template_parameter
/// \notes The `default_type` may be `nullptr` in which case the parameter has no default.
static std::unique_ptr<cpp_template_type_parameter> build(
const cpp_entity_index& idx, cpp_entity_id id, std::string name, cpp_template_keyword kw,
bool variadic, std::unique_ptr<cpp_type> default_type = nullptr);
bool variadic, std::unique_ptr<cpp_type> default_type = nullptr,
type_safe::optional<cpp_token_string> concept_constraint = type_safe::nullopt);

/// \returns A [ts::optional_ref]() to the default type.
type_safe::optional_ref<const cpp_type> default_type() const noexcept
Expand All @@ -66,17 +68,24 @@ class cpp_template_type_parameter final : public cpp_template_parameter
return keyword_;
}

const type_safe::optional<cpp_token_string>& concept_constraint() const noexcept
{
return concept_constraint_;
}

private:
cpp_template_type_parameter(std::string name, cpp_template_keyword kw, bool variadic,
std::unique_ptr<cpp_type> default_type)
std::unique_ptr<cpp_type> default_type,
type_safe::optional<cpp_token_string> concept_constraint)
: cpp_template_parameter(std::move(name), variadic), default_type_(std::move(default_type)),
keyword_(kw)
keyword_(kw), concept_constraint_(concept_constraint)
{}

cpp_entity_kind do_get_entity_kind() const noexcept override;

std::unique_ptr<cpp_type> default_type_;
cpp_template_keyword keyword_;
type_safe::optional<cpp_token_string> concept_constraint_;
};

/// \exclude
Expand Down
1 change: 1 addition & 0 deletions include/cppast/cppast_fwd.hpp
Expand Up @@ -20,6 +20,7 @@ class cpp_builtin_type;
class cpp_class;
class cpp_class_template;
class cpp_class_template_specialization;
class cpp_concept;
class cpp_constructor;
class cpp_conversion_op;
class cpp_cv_qualified_type;
Expand Down
3 changes: 3 additions & 0 deletions src/CMakeLists.txt
Expand Up @@ -13,6 +13,7 @@ set(header
../include/cppast/cpp_attribute.hpp
../include/cppast/cpp_class.hpp
../include/cppast/cpp_class_template.hpp
../include/cppast/cpp_concept.hpp
../include/cppast/cpp_decltype_type.hpp
../include/cppast/cpp_entity.hpp
../include/cppast/cpp_entity_container.hpp
Expand Down Expand Up @@ -54,6 +55,7 @@ set(source
cpp_attribute.cpp
cpp_class.cpp
cpp_class_template.cpp
cpp_concept.cpp
cpp_entity.cpp
cpp_entity_index.cpp
cpp_entity_kind.cpp
Expand All @@ -80,6 +82,7 @@ set(source
visitor.cpp)
set(libclang_source
libclang/class_parser.cpp
libclang/concept_parser.cpp
libclang/cxtokenizer.cpp
libclang/cxtokenizer.hpp
libclang/debug_helper.cpp
Expand Down
24 changes: 23 additions & 1 deletion src/code_generator.cpp
Expand Up @@ -6,6 +6,7 @@
#include <cppast/cpp_alias_template.hpp>
#include <cppast/cpp_class.hpp>
#include <cppast/cpp_class_template.hpp>
#include <cppast/cpp_concept.hpp>
#include <cppast/cpp_entity_kind.hpp>
#include <cppast/cpp_enum.hpp>
#include <cppast/cpp_file.hpp>
Expand Down Expand Up @@ -879,7 +880,10 @@ bool generate_template_type_parameter(code_generator& generat
code_generator::output output(type_safe::ref(generator), type_safe::ref(param), cur_access);
if (output)
{
output << keyword(to_string(param.keyword()));
if(param.keyword() == cpp_template_keyword::concept_contraint)
detail::write_token_string(output, param.concept_constraint().value());
else
output << keyword(to_string(param.keyword()));
if (param.is_variadic())
output << operator_ws << punctuation("...");
if (!param.name().empty())
Expand Down Expand Up @@ -1036,6 +1040,23 @@ bool generate_class_template_specialization(code_generator&
return static_cast<bool>(output);
}

bool generate_concept(code_generator& generator,
const cpp_concept& con,
cpp_access_specifier_kind cur_access)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(con), cur_access);
if(output)
{
output << keyword("template") << operator_ws << punctuation("<") << bracket_ws;
detail::write_token_string(output, con.parameters());
output << bracket_ws << punctuation(">") << newl;
output << keyword("concept") << operator_ws << identifier(con.name()) << operator_ws << punctuation("=") << operator_ws;
detail::write_expression(output, con.constraint_expression());
output << operator_ws << punctuation(";") << newl;
}
return static_cast<bool>(output);
}

bool generate_static_assert(code_generator& generator, const cpp_static_assert& assert,
cpp_access_specifier_kind cur_access)
{
Expand Down Expand Up @@ -1115,6 +1136,7 @@ bool generate_code_impl(code_generator& generator, const cpp_entity& e,
CPPAST_DETAIL_HANDLE(function_template_specialization)
CPPAST_DETAIL_HANDLE(class_template)
CPPAST_DETAIL_HANDLE(class_template_specialization)
CPPAST_DETAIL_HANDLE(concept)

CPPAST_DETAIL_HANDLE(static_assert)

Expand Down
24 changes: 24 additions & 0 deletions src/cpp_concept.cpp
@@ -0,0 +1,24 @@
// Copyright (C) 2017-2022 Jonathan Müller and cppast contributors
// SPDX-License-Identifier: MIT

#include <cppast/cpp_concept.hpp>

#include <cppast/cpp_entity_kind.hpp>

using namespace cppast;

cpp_entity_kind cppast::cpp_concept::kind() noexcept
{
return cpp_entity_kind::concept_t;
}

cpp_entity_kind cpp_concept::do_get_entity_kind() const noexcept
{
return cpp_entity_kind::concept_t;
}

std::unique_ptr<cpp_concept> cpp_concept::builder::finish(const cpp_entity_index& idx, cpp_entity_id id)
{
idx.register_definition(id, type_safe::ref(*concept_));
return std::move(concept_);
}
6 changes: 6 additions & 0 deletions src/cpp_entity_kind.cpp
Expand Up @@ -88,6 +88,8 @@ const char* cppast::to_string(cpp_entity_kind kind) noexcept
return "class template";
case cpp_entity_kind::class_template_specialization_t:
return "class template specialization";
case cpp_entity_kind::concept_t:
return "concept";

case cpp_entity_kind::static_assert_t:
return "static_assert";
Expand Down Expand Up @@ -142,6 +144,7 @@ bool cppast::is_function(cpp_entity_kind kind) noexcept
case cpp_entity_kind::function_template_specialization_t:
case cpp_entity_kind::class_template_t:
case cpp_entity_kind::class_template_specialization_t:
case cpp_entity_kind::concept_t:
case cpp_entity_kind::static_assert_t:
case cpp_entity_kind::unexposed_t:
case cpp_entity_kind::count:
Expand Down Expand Up @@ -191,6 +194,7 @@ bool cppast::is_parameter(cpp_entity_kind kind) noexcept
case cpp_entity_kind::function_template_specialization_t:
case cpp_entity_kind::class_template_t:
case cpp_entity_kind::class_template_specialization_t:
case cpp_entity_kind::concept_t:
case cpp_entity_kind::static_assert_t:
case cpp_entity_kind::unexposed_t:
case cpp_entity_kind::count:
Expand All @@ -209,6 +213,7 @@ bool cppast::is_template(cpp_entity_kind kind) noexcept
case cpp_entity_kind::function_template_specialization_t:
case cpp_entity_kind::class_template_t:
case cpp_entity_kind::class_template_specialization_t:
case cpp_entity_kind::concept_t:
return true;

case cpp_entity_kind::file_t:
Expand Down Expand Up @@ -288,6 +293,7 @@ bool cppast::is_template_specialization(cpp_entity_kind kind) noexcept
case cpp_entity_kind::variable_template_t:
case cpp_entity_kind::function_template_t:
case cpp_entity_kind::class_template_t:
case cpp_entity_kind::concept_t:
case cpp_entity_kind::static_assert_t:
case cpp_entity_kind::unexposed_t:
case cpp_entity_kind::count:
Expand Down
1 change: 1 addition & 0 deletions src/cpp_forward_declarable.cpp
Expand Up @@ -56,6 +56,7 @@ type_safe::optional_ref<const cpp_forward_declarable> get_declarable(const cpp_e
case cpp_entity_kind::template_type_parameter_t:
case cpp_entity_kind::non_type_template_parameter_t:
case cpp_entity_kind::template_template_parameter_t:
case cpp_entity_kind::concept_t:
case cpp_entity_kind::alias_template_t:
case cpp_entity_kind::variable_template_t:
case cpp_entity_kind::static_assert_t:
Expand Down
7 changes: 5 additions & 2 deletions src/cpp_template_parameter.cpp
Expand Up @@ -15,17 +15,20 @@ const char* cppast::to_string(cpp_template_keyword kw) noexcept
return "class";
case cpp_template_keyword::keyword_typename:
return "typename";
case cpp_template_keyword::concept_contraint:
return "concept constraint data lost";
}

return "should not get here";
}

std::unique_ptr<cpp_template_type_parameter> cpp_template_type_parameter::build(
const cpp_entity_index& idx, cpp_entity_id id, std::string name, cpp_template_keyword kw,
bool variadic, std::unique_ptr<cpp_type> default_type)
bool variadic, std::unique_ptr<cpp_type> default_type,
type_safe::optional<cpp_token_string> concept_constraint)
{
std::unique_ptr<cpp_template_type_parameter> result(
new cpp_template_type_parameter(std::move(name), kw, variadic, std::move(default_type)));
new cpp_template_type_parameter(std::move(name), kw, variadic, std::move(default_type), std::move(concept_constraint)));
idx.register_definition(std::move(id), type_safe::cref(*result));
return result;
}
Expand Down
1 change: 1 addition & 0 deletions src/cpp_type.cpp
Expand Up @@ -148,6 +148,7 @@ bool detail::cpp_type_ref_predicate::operator()(const cpp_entity& e)
case cpp_entity_kind::function_template_specialization_t:
case cpp_entity_kind::class_template_t:
case cpp_entity_kind::class_template_specialization_t:
case cpp_entity_kind::concept_t:
case cpp_entity_kind::static_assert_t:
case cpp_entity_kind::unexposed_t:
case cpp_entity_kind::count:
Expand Down
62 changes: 62 additions & 0 deletions src/libclang/concept_parser.cpp
@@ -0,0 +1,62 @@
// Copyright (C) 2017-2022 Jonathan Müller and cppast contributors
// SPDX-License-Identifier: MIT

#include <cppast/cpp_concept.hpp>

#include <cppast/cpp_entity_kind.hpp>

#include "libclang_visitor.hpp"
#include "parse_functions.hpp"

using namespace cppast;

std::unique_ptr<cpp_entity> detail::try_parse_cpp_concept(const detail::parse_context& context,
const CXCursor& cur)
{
DEBUG_ASSERT(cur.kind == CXCursor_UnexposedDecl, detail::assert_handler{});

detail::cxtokenizer tokenizer(context.tu, context.file, cur);
detail::cxtoken_stream stream(tokenizer, cur);

if (!detail::skip_if(stream, "template"))
return nullptr;

if (stream.peek() != "<")
return nullptr;


auto closing_bracket_iter = detail::find_closing_bracket(stream);
auto params = to_string(stream, closing_bracket_iter);

if (!detail::skip_if(stream, ">"))
return nullptr;

if (!detail::skip_if(stream, "concept"))
return nullptr;

const auto& identifier_token = stream.get();
if (identifier_token.kind() != CXTokenKind::CXToken_Identifier)
{
return nullptr;
}

cpp_concept::builder builder(identifier_token.value().std_str());

if (!detail::skip_if(stream, "="))
{
return nullptr;
}

if (*(stream.end() - 1) != ";")
{
return nullptr;
}

builder.set_expression(
parse_raw_expression(context, stream, stream.end() - 1,
cpp_builtin_type::build(cpp_builtin_type_kind::cpp_bool)));

builder.set_parameters(std::move(params));

return builder.finish(*context.idx, detail::get_entity_id(cur));
}

0 comments on commit a937efb

Please sign in to comment.