diff --git a/include/cppast/code_generator.hpp b/include/cppast/code_generator.hpp new file mode 100644 index 0000000..fa7d00a --- /dev/null +++ b/include/cppast/code_generator.hpp @@ -0,0 +1,421 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#ifndef CPPAST_CODE_GENERATOR_HPP_INCLUDED +#define CPPAST_CODE_GENERATOR_HPP_INCLUDED + +#include + +#include + +#include +#include + +namespace cppast +{ + /// A simple string view implementation, like [std::string_view](). + /// + /// It "views" - stores a pointer to - some kind of string. + class string_view + { + public: + /// \effects Creates it viewing the [std::string](). + string_view(const std::string& str) noexcept : str_(str.c_str()), length_(str.length()) + { + } + + /// \effects Creates it viewing the C string `str`. + constexpr string_view(const char* str) noexcept : str_(str), length_(std::strlen(str)) + { + } + + /// \effects Creates it viewing the C string literal. + template + constexpr string_view(const char (&str)[Size]) noexcept : str_(str), length_(Size - 1u) + { + } + + /// \returns The number of characters. + constexpr std::size_t length() const noexcept + { + return length_; + } + + /// \returns The C string. + constexpr const char* c_str() const noexcept + { + return str_; + } + + /// \returns The character at the given index. + /// \requires `i < length()`. + char operator[](type_safe::index_t i) const noexcept + { + return type_safe::at(str_, i); + } + + private: + const char* str_; + std::size_t length_; + }; + + /// \exclude + namespace detail + { + template + class semantic_string_view + { + public: + template + explicit semantic_string_view(T&& obj, + decltype(string_view(std::forward(obj)), 0) = 0) + : str_(std::forward(obj)) + { + } + + const string_view& str() const noexcept + { + return str_; + } + + private: + string_view str_; + }; + } // namespace detail + + /// A special [cppast::string_view]() representing a C++ keyword token. + using keyword = detail::semantic_string_view; + + /// A special [cppast::string_view]() representing a C++ identifier token. + using identifier = detail::semantic_string_view; + + /// A special [cppast::string_view]() representing a C++ string or character literal token. + using string_literal = detail::semantic_string_view; + + /// A special [cppast::string_view]() representing a C++ integer literal token. + using int_literal = detail::semantic_string_view; + + /// A special [cppast::string_view]() representing a C++ floating point literal token. + using float_literal = detail::semantic_string_view; + + /// A special [cppast::string_view]() representing a C++ punctuation token like `.` or `(`. + using punctuation = detail::semantic_string_view; + + /// A special [cppast::string_view]() representing a C++ preprocessor token. + using preprocessor_token = detail::semantic_string_view; + + /// A special [cppast::string_view]() representing a sequence of unknown C++ tokens. + using token_seq = detail::semantic_string_view; + + /// Tag type to represent an end-of-line character. + constexpr struct newl_t + { + constexpr newl_t() noexcept + { + } + } newl; + + /// Tag type to represent a single space character. + constexpr struct whitespace_t + { + constexpr whitespace_t() noexcept + { + } + } whitespace; + + /// Base class to control the code generation. + /// + /// Inherit from it to customize how a [cppast::cpp_entity]() is printed + /// by [cppast::generate_code](). + class code_generator + { + public: + code_generator(const code_generator&) = delete; + code_generator& operator=(const code_generator&) = delete; + virtual ~code_generator() noexcept = default; + + /// Sentinel type used to output a given entity. + class output + { + public: + /// \effects Creates it giving the generator, the entity + /// and whether or not the entity is a container. + /// It is a container if while this object lives + /// any other `output` objects are created. + /// + /// It will call `on_container_begin()` or `on_leaf()`, + /// respectively. + explicit output(type_safe::object_ref gen, + type_safe::object_ref e, bool is_container) + : gen_(gen), print_(is_container ? gen_->on_container_begin(*e) : gen_->on_leaf(*e)) + { + if (is_container) + e_ = e; + } + + /// \effects If the entity is a container + /// and `on_container_begin()` returned `true`, + /// calls `on_container_end()`, + /// else does nothing. + ~output() noexcept + { + if (print_ && e_) + gen_->on_container_end(e_.value()); + } + + output(const output&) = delete; + output& operator=(const output&) = delete; + + /// \returns Whether or not the `on_XXX` function returned `true`. + /// \notes If this returns `false`, + /// the other functions have no effects. + explicit operator bool() const noexcept + { + return print_; + } + + /// \returns A reference to the generator. + type_safe::object_ref generator() const noexcept + { + return gen_; + } + + /// \effects Call `do_indent()` followed by `do_write_newline()` (if `print_newline` is `true`). + void indent(bool print_newline = true) const noexcept + { + if (print_) + { + gen_->do_indent(); + if (print_newline) + gen_->do_write_newline(); + } + } + + /// \effects Calls `do_unindent()`. + void unindent() const noexcept + { + if (print_) + gen_->do_unindent(); + } + + /// \effects Calls `do_write_keyword()`. + const output& operator<<(const keyword& k) const + { + if (print_) + gen_->do_write_keyword(k.str()); + return *this; + } + + /// \effects Calls `do_write_identifier()`. + const output& operator<<(const identifier& ident) const + { + if (print_) + gen_->do_write_identifier(ident.str()); + return *this; + } + + /// \effects Calls `do_write_reference()`. + template + const output& operator<<(const basic_cpp_entity_ref& ref) const + { + if (print_) + gen_->do_write_reference(ref.id(), ref.name()); + return *this; + } + + /// \effects Calls `do_write_punctuation()`. + const output& operator<<(const punctuation& punct) const + { + if (print_) + gen_->do_write_punctuation(punct.str()); + return *this; + } + + /// \effects Calls `do_write_str_literal`. + const output& operator<<(const string_literal& lit) const + { + if (print_) + gen_->do_write_str_literal(lit.str()); + return *this; + } + + /// \effects Calls `do_write_int_literal()`. + const output& operator<<(const int_literal& lit) const + { + if (print_) + gen_->do_write_int_literal(lit.str()); + return *this; + } + + /// \effects Calls `do_write_float_literal()`. + const output& operator<<(const float_literal& lit) const + { + if (print_) + gen_->do_write_float_literal(lit.str()); + return *this; + } + + /// \effects Calls `do_write_preprocessor()`. + const output& operator<<(const preprocessor_token& tok) const + { + if (print_) + gen_->do_write_preprocessor(tok.str()); + return *this; + } + + /// \effects Calls `do_write_token_seq()`. + const output& operator<<(const token_seq& seq) const + { + if (print_) + gen_->do_write_token_seq(seq.str()); + return *this; + } + + /// \effects Calls `do_write_newline()`. + const output& operator<<(newl_t) const + { + if (print_) + gen_->do_write_newline(); + return *this; + } + + /// \effects Calls `do_write_whitespace()`. + const output& operator<<(whitespace_t) const + { + if (print_) + gen_->do_write_whitespace(); + return *this; + } + + private: + type_safe::object_ref gen_; + type_safe::optional_ref e_; + bool print_; + }; + + protected: + code_generator() noexcept = default; + + private: + /// \effects Will be invoked before code of a container entity is generated. + /// The base class version has no effect. + /// \returns Whether or not that entity should actually be generated. + /// The base class version returns `true`. + virtual bool on_container_begin(const cpp_entity& e) + { + (void)e; + return true; + } + + /// \effects Will be invoked after all code of a container entity has been generated. + /// The base class version has no effect. + virtual void on_container_end(const cpp_entity& e) + { + (void)e; + } + + /// \effects Will be invoked before code of a non-container entity is generated. + /// The base class version has no effect. + /// \returns Whether or not that entity should actually be generated. + /// The base class version returns `true`. + virtual bool on_leaf(const cpp_entity& e) + { + (void)e; + return true; + } + + /// \effects Will be invoked when the indentation level should be increased by one. + /// The level change must be applied on the next call to `do_write_newline()`. + virtual void do_indent() = 0; + + /// \effects Will be invoked when the indentation level should be decreased by one. + /// The level change must be applied immediately if nothing else has been written on the current line. + virtual void do_unindent() = 0; + + /// \effects Writes the given token sequence. + virtual void do_write_token_seq(string_view tokens) = 0; + + /// \effects Writes the specified special token. + /// The base class version simply forwards to `do_write_token_seq()`. + /// \notes This is useful for syntax highlighting, for example. + /// \group write + virtual void do_write_keyword(string_view keyword) + { + do_write_token_seq(keyword); + } + /// \group write + virtual void do_write_identifier(string_view identifier) + { + do_write_token_seq(identifier); + } + /// \group write + virtual void do_write_reference(type_safe::array_ref id, + string_view name) + { + (void)id; + do_write_token_seq(name); + } + /// \group write + virtual void do_write_punctuation(string_view punct) + { + do_write_token_seq(punct); + } + /// \group write + virtual void do_write_str_literal(string_view str) + { + do_write_token_seq(str); + } + /// \group write + virtual void do_write_int_literal(string_view str) + { + do_write_token_seq(str); + } + /// \group write + virtual void do_write_float_literal(string_view str) + { + do_write_token_seq(str); + } + /// \group write + virtual void do_write_preprocessor(string_view punct) + { + do_write_token_seq(punct); + } + + /// \effects Writes a newline. + /// It is guaranteed that this is the only way a newline will be printed. + /// The base class forwards to `do_write_token_seq()`. + virtual void do_write_newline() + { + do_write_token_seq("\n"); + } + /// \effects Writes a whitespace character. + /// It will be invoked only where a whitespace is truly needed, + /// like between two keywords. + /// The base class forwards to `do_write_token_seq()`. + virtual void do_write_whitespace() + { + do_write_token_seq(" "); + } + }; + + /// Generates code for the given entity. + /// + /// How the code is generated is customized by the generator. + /// The implementation will write whitespace only where necessary, + /// but a newline after each entity. + /// This allows customization of formatting. + void generate_code(code_generator& generator, const cpp_entity& e); + + /// \exclude + class cpp_template_argument; + + /// \exclude + namespace detail + { + void write_template_arguments(code_generator::output& output, + type_safe::array_ref arguments); + } // namespace detail +} // namespace cppast + +#endif // CPPAST_CODE_GENERATOR_HPP_INCLUDED diff --git a/include/cppast/cpp_array_type.hpp b/include/cppast/cpp_array_type.hpp index 2db9edd..bea4ac1 100644 --- a/include/cppast/cpp_array_type.hpp +++ b/include/cppast/cpp_array_type.hpp @@ -44,7 +44,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::array; + return cpp_type_kind::array_t; } std::unique_ptr type_; diff --git a/include/cppast/cpp_decltype_type.hpp b/include/cppast/cpp_decltype_type.hpp index 561d32e..f475264 100644 --- a/include/cppast/cpp_decltype_type.hpp +++ b/include/cppast/cpp_decltype_type.hpp @@ -33,7 +33,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::decltype_; + return cpp_type_kind::decltype_t; } std::unique_ptr expr_; @@ -54,7 +54,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::decltype_auto; + return cpp_type_kind::decltype_auto_t; } }; } // namespace cppast diff --git a/include/cppast/cpp_expression.hpp b/include/cppast/cpp_expression.hpp index 8b8dbc0..9bcbb5c 100644 --- a/include/cppast/cpp_expression.hpp +++ b/include/cppast/cpp_expression.hpp @@ -14,9 +14,9 @@ namespace cppast /// The kind of a [cppast::cpp_expression](). enum class cpp_expression_kind { - literal, + literal_t, - unexposed, + unexposed_t, }; /// Base class for all C++ expressions. @@ -83,7 +83,7 @@ namespace cppast cpp_expression_kind do_get_kind() const noexcept override { - return cpp_expression_kind::unexposed; + return cpp_expression_kind::unexposed_t; } std::string str_; @@ -115,11 +115,17 @@ namespace cppast cpp_expression_kind do_get_kind() const noexcept override { - return cpp_expression_kind::literal; + return cpp_expression_kind::literal_t; } std::string value_; }; + + /// \exclude + namespace detail + { + void write_expression(code_generator::output& output, const cpp_expression& expr); + } // namespace detail } // namespace cppast #endif // CPPAST_CPP_EXPRESSION_HPP_INCLUDED diff --git a/include/cppast/cpp_function_type.hpp b/include/cppast/cpp_function_type.hpp index 091c8d0..a7b9aa9 100644 --- a/include/cppast/cpp_function_type.hpp +++ b/include/cppast/cpp_function_type.hpp @@ -73,7 +73,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::function; + return cpp_type_kind::function_t; } std::unique_ptr return_type_; @@ -154,7 +154,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::member_function; + return cpp_type_kind::member_function_t; } std::unique_ptr class_type_, return_type_; @@ -197,7 +197,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::member_object; + return cpp_type_kind::member_object_t; } std::unique_ptr class_type_, object_type_; diff --git a/include/cppast/cpp_template.hpp b/include/cppast/cpp_template.hpp index e481045..c5155d6 100644 --- a/include/cppast/cpp_template.hpp +++ b/include/cppast/cpp_template.hpp @@ -165,7 +165,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::template_instantiation; + return cpp_type_kind::template_instantiation_t; } type_safe::variant, std::string> arguments_; diff --git a/include/cppast/cpp_template_parameter.hpp b/include/cppast/cpp_template_parameter.hpp index dae27e1..a4a3ffa 100644 --- a/include/cppast/cpp_template_parameter.hpp +++ b/include/cppast/cpp_template_parameter.hpp @@ -124,7 +124,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::template_parameter; + return cpp_type_kind::template_parameter_t; } cpp_template_type_parameter_ref parameter_; diff --git a/include/cppast/cpp_type.hpp b/include/cppast/cpp_type.hpp index 6a0936f..7da60ed 100644 --- a/include/cppast/cpp_type.hpp +++ b/include/cppast/cpp_type.hpp @@ -9,34 +9,35 @@ #include #include +#include namespace cppast { /// The kinds of a [cppast::cpp_type](). enum class cpp_type_kind { - builtin, - user_defined, + builtin_t, + user_defined_t, - auto_, - decltype_, - decltype_auto, + auto_t, + decltype_t, + decltype_auto_t, - cv_qualified, - pointer, - reference, + cv_qualified_t, + pointer_t, + reference_t, - array, - function, - member_function, - member_object, + array_t, + function_t, + member_function_t, + member_object_t, - template_parameter, - template_instantiation, + template_parameter_t, + template_instantiation_t, - dependent, + dependent_t, - unexposed, + unexposed_t, }; /// Base class for all C++ types. @@ -95,7 +96,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::unexposed; + return cpp_type_kind::unexposed_t; } std::string name_; @@ -163,7 +164,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::builtin; + return cpp_type_kind::builtin_t; } cpp_builtin_type_kind kind_; @@ -207,7 +208,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::user_defined; + return cpp_type_kind::user_defined_t; } cpp_type_ref entity_; @@ -228,7 +229,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::auto_; + return cpp_type_kind::auto_t; } }; @@ -269,7 +270,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::dependent; + return cpp_type_kind::dependent_t; } std::string name_; @@ -331,7 +332,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::cv_qualified; + return cpp_type_kind::cv_qualified_t; } std::unique_ptr type_; @@ -361,7 +362,7 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::pointer; + return cpp_type_kind::pointer_t; } std::unique_ptr pointee_; @@ -409,12 +410,31 @@ namespace cppast cpp_type_kind do_get_kind() const noexcept override { - return cpp_type_kind::reference; + return cpp_type_kind::reference_t; } std::unique_ptr referee_; cpp_reference ref_; }; + + /// \exclude + namespace detail + { + // whether or not it requires special treatment when printing it + // i.e. function pointer types are complex as the identifier has to be put inside + bool is_complex_type(const cpp_type& type) noexcept; + + // write part of the type that comes before the variable name + void write_type_prefix(code_generator::output& output, const cpp_type& type); + + // write part of the type that comes after the name + // for non-complex types, this does nothing + void write_type_suffix(code_generator::output& output, const cpp_type& type); + + // write prefix, variadic, name, suffix + void write_type(code_generator::output& output, const cpp_type& type, std::string name, + bool is_variadic = false); + } // namespace detail } // namespace cppast #endif // CPPAST_CPP_TYPE_HPP_INCLUDED diff --git a/src/code_generator.cpp b/src/code_generator.cpp new file mode 100644 index 0000000..a9a4525 --- /dev/null +++ b/src/code_generator.cpp @@ -0,0 +1,899 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace cppast; + +namespace +{ + template + auto write_sep(code_generator::output& output, Sep s) -> decltype(output << s) + { + return output << s; + } + + template + auto write_sep(code_generator::output& output, Sep s) -> decltype(s(output)) + { + return s(output); + } + + template + bool write_container(code_generator::output& output, const Container& cont, Sep s) + { + auto need_sep = false; + for (auto& child : cont) + { + if (need_sep) + write_sep(output, s); + else + need_sep = true; + generate_code(*output.generator(), child); + } + return need_sep; + } + + void generate_file(code_generator& generator, const cpp_file& f) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(f), true); + if (output) + { + write_container(output, f, newl); + output << newl; + } + } + + void generate_macro_definition(code_generator& generator, const cpp_macro_definition& def) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(def), false); + if (output) + { + output << preprocessor_token("#define") << whitespace << identifier(def.name()); + if (def.is_function_like()) + output << preprocessor_token("(") << preprocessor_token(def.parameters().value()) + << preprocessor_token(")"); + if (!def.replacement().empty()) + output << whitespace << preprocessor_token(def.replacement()) << newl; + else + output << newl; + } + } + + void generate_include_directive(code_generator& generator, const cpp_include_directive& include) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(include), false); + if (output) + { + output << preprocessor_token("#include") << whitespace; + if (include.include_kind() == cpp_include_kind::system) + output << preprocessor_token("<"); + else + output << preprocessor_token("\""); + output << include.target(); + if (include.include_kind() == cpp_include_kind::system) + output << preprocessor_token(">"); + else + output << preprocessor_token("\""); + output << newl; + } + } + + void generate_language_linkage(code_generator& generator, const cpp_language_linkage& linkage) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(linkage), true); + if (output) + { + output << keyword("extern") << whitespace << string_literal(linkage.name()); + if (linkage.is_block()) + { + output << punctuation("{"); + output.indent(); + + write_container(output, linkage, newl); + + output.unindent(); + output << punctuation("}") << newl; + } + else + { + output << whitespace; + generate_code(generator, *linkage.begin()); + } + } + } + + void generate_namespace(code_generator& generator, const cpp_namespace& ns) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(ns), true); + if (output) + { + if (ns.is_inline()) + output << keyword("inline") << whitespace; + output << keyword("namespace") << whitespace << identifier(ns.name()); + output << punctuation("{"); + output.indent(); + + write_container(output, ns, newl); + + output.unindent(); + output << punctuation("}") << newl; + } + } + + void generate_namespace_alias(code_generator& generator, const cpp_namespace_alias& alias) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false); + if (output) + output << keyword("namespace") << whitespace << identifier(alias.name()) + << punctuation("=") << alias.target() << punctuation(";") << newl; + } + + void generate_using_directive(code_generator& generator, const cpp_using_directive& directive) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(directive), false); + if (output) + output << keyword("using") << whitespace << keyword("namespace") << whitespace + << directive.target() << punctuation(";") << newl; + } + + void generate_using_declaration(code_generator& generator, + const cpp_using_declaration& declaration) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(declaration), + false); + if (output) + output << keyword("using") << whitespace << declaration.target() << punctuation(";") + << newl; + } + + void generate_type_alias(code_generator& generator, const cpp_type_alias& alias) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false); + if (output) + { + output << keyword("using") << whitespace << identifier(alias.name()) + << punctuation("="); + detail::write_type(output, alias.underlying_type(), ""); + output << punctuation(";") << newl; + } + } + + void generate_enum_value(code_generator& generator, const cpp_enum_value& value) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(value), false); + if (output) + { + output << identifier(value.name()); + if (value.value()) + { + output << punctuation("="); + detail:: + write_expression(output, + value.value() + .value()); // should have named something differently... + } + } + } + + void generate_enum(code_generator& generator, const cpp_enum& e) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(e), true); + if (output) + { + output << keyword("enum"); + if (e.is_scoped()) + output << whitespace << keyword("class"); + output << whitespace << identifier(e.name()); + if (e.underlying_type()) + { + output << newl << punctuation(":"); + detail::write_type(output, e.underlying_type().value(), ""); + } + + if (e.is_definition()) + { + output << punctuation("{"); + output.indent(); + + auto need_sep = write_container(output, e, [](code_generator::output& out) { + out << punctuation(",") << newl; + }); + if (need_sep) + output << newl; + + output.unindent(); + output << punctuation("};") << newl; + } + else + output << punctuation(";") << newl; + } + } + + void generate_access_specifier(code_generator& generator, const cpp_access_specifier& access) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(access), false); + if (output) + { + output.unindent(); + output << keyword(to_string(access.access_specifier())) << punctuation(":"); + output.indent(false); + } + } + + void generate_base_class(code_generator& generator, const cpp_base_class& base) + { + DEBUG_ASSERT(base.parent() && base.parent().value().kind() == cpp_entity_kind::class_t, + detail::assert_handler{}); + auto parent_kind = static_cast(base.parent().value()).class_kind(); + + code_generator::output output(type_safe::ref(generator), type_safe::ref(base), false); + if (output) + { + if (base.is_virtual()) + output << keyword("virtual") << whitespace; + + auto access = base.access_specifier(); + if (access == cpp_protected) + output << keyword("protected") << whitespace; + else if (access == cpp_private && parent_kind != cpp_class_kind::class_t) + output << keyword("private") << whitespace; + else if (access == cpp_public && parent_kind == cpp_class_kind::class_t) + output << keyword("public") << whitespace; + + output << identifier(base.name()); + } + } + + void write_specialization_arguments(code_generator::output& output, + const cpp_template_specialization& spec) + { + if (spec.arguments_exposed()) + detail::write_template_arguments(output, spec.arguments()); + else if (!spec.unexposed_arguments().empty()) + output << punctuation("<") << token_seq(spec.unexposed_arguments()) << punctuation(">"); + } + + void generate_class(code_generator& generator, const cpp_class& c, + type_safe::optional_ref spec = nullptr) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(c), true); + if (output) + { + output << keyword(to_string(c.class_kind())) << whitespace; + + if (spec) + { + output << spec.value().primary_template(); + write_specialization_arguments(output, spec.value()); + } + else + output << identifier(c.name()); + + if (c.is_final()) + output << whitespace << keyword("final"); + + if (c.is_declaration()) + output << punctuation(";") << newl; + else + { + if (!c.bases().empty()) + { + output << newl << punctuation(":"); + + auto need_sep = false; + for (auto& base : c.bases()) + { + if (need_sep) + output << punctuation(","); + else + need_sep = true; + generate_base_class(generator, base); + } + } + output << punctuation("{"); + output.indent(); + + auto need_sep = false; + auto last_access = + c.class_kind() == cpp_class_kind::class_t ? cpp_private : cpp_public; + for (auto& member : c) + { + if (member.kind() == cpp_entity_kind::access_specifier_t) + { + auto& access = static_cast(member); + if (access.access_specifier() != last_access) + { + if (need_sep) + output << newl; + else + need_sep = true; + generate_access_specifier(generator, access); + last_access = access.access_specifier(); + } + } + else + { + if (need_sep) + output << newl; + else + need_sep = true; + generate_code(generator, member); + } + } + + output.unindent(); + output << punctuation("};") << newl; + } + } + } + + void write_variable_base(code_generator::output& output, const cpp_variable_base& var, + const std::string& name) + { + detail::write_type(output, var.type(), name); + + if (var.default_value()) + { + output << punctuation("="); + detail::write_expression(output, var.default_value().value()); + } + } + + void write_storage_class(code_generator::output& output, cpp_storage_class_specifiers storage, + bool is_constexpr) + { + if (is_static(storage)) + output << keyword("static") << whitespace; + if (is_extern(storage)) + output << keyword("extern") << whitespace; + if (is_thread_local(storage)) + output << keyword("thread_local") << whitespace; + if (is_constexpr) + output << keyword("constexpr") << whitespace; + } + + void generate_variable(code_generator& generator, const cpp_variable& var) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false); + if (output) + { + write_storage_class(output, var.storage_class(), var.is_constexpr()); + + write_variable_base(output, var, var.name()); + output << punctuation(";") << newl; + } + } + + void generate_member_variable(code_generator& generator, const cpp_member_variable& var) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false); + if (output) + { + if (var.is_mutable()) + output << keyword("mutable") << whitespace; + write_variable_base(output, var, var.name()); + output << punctuation(";") << newl; + } + } + + void generate_bitfield(code_generator& generator, const cpp_bitfield& var) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false); + if (output) + { + if (var.is_mutable()) + output << keyword("mutable") << whitespace; + write_variable_base(output, var, var.name()); + output << punctuation(":") << int_literal(std::to_string(var.no_bits())); + output << punctuation(";") << newl; + } + } + + void generate_function_parameter(code_generator& generator, const cpp_function_parameter& param) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(param), false); + if (output) + write_variable_base(output, param, param.name()); + } + + void write_function_parameters(code_generator::output& output, const cpp_function_base& base) + { + output << punctuation("("); + auto need_sep = write_container(output, base, punctuation(",")); + if (base.is_variadic()) + { + if (need_sep) + output << punctuation(","); + output << punctuation("..."); + } + output << punctuation(")"); + } + + void write_noexcept(code_generator::output& output, const cpp_function_base& base, bool need_ws) + { + if (!base.noexcept_condition()) + return; + else if (need_ws) + output << whitespace; + + auto& cond = base.noexcept_condition().value(); + if (cond.kind() == cpp_expression_kind::literal_t + && static_cast(cond).value() == "true") + output << keyword("noexcept"); + else + { + output << keyword("noexcept") << punctuation("("); + detail::write_expression(output, cond); + output << punctuation(")"); + } + } + + void write_function_body(code_generator::output& output, const cpp_function_base& base, + bool is_pure_virtual) + { + switch (base.body_kind()) + { + case cpp_function_declaration: + case cpp_function_definition: + if (is_pure_virtual) + output << punctuation("=") << int_literal("0"); + output << punctuation(";") << newl; + break; + + case cpp_function_defaulted: + output << punctuation("=") << keyword("default") << punctuation(";") << newl; + break; + case cpp_function_deleted: + output << punctuation("=") << keyword("delete") << punctuation(";") << newl; + break; + } + } + + void generate_function( + code_generator& generator, const cpp_function& func, + type_safe::optional_ref spec = nullptr) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true); + if (output) + { + if (func.is_friend()) + output << keyword("friend") << whitespace; + write_storage_class(output, func.storage_class(), func.is_constexpr()); + + if (detail::is_complex_type(func.return_type())) + output << keyword("auto") << whitespace; + else + { + detail::write_type(output, func.return_type(), ""); + output << whitespace; + } + + if (spec) + { + output << spec.value().primary_template(); + write_specialization_arguments(output, spec.value()); + } + else + output << identifier(func.name()); + write_function_parameters(output, func); + write_noexcept(output, func, false); + + if (detail::is_complex_type(func.return_type())) + { + output << punctuation("->"); + detail::write_type(output, func.return_type(), ""); + } + write_function_body(output, func, false); + } + } + + void write_prefix_virtual(code_generator::output& output, const cpp_virtual& virt) + { + if (is_virtual(virt)) + output << keyword("virtual") << whitespace; + } + + void write_suffix_virtual(code_generator::output& output, const cpp_virtual& virt) + { + if (is_overriding(virt)) + output << whitespace << keyword("override"); + if (is_final(virt)) + output << whitespace << keyword("final"); + } + + bool write_cv_ref(code_generator::output& output, const cpp_member_function_base& base) + { + auto need_ws = false; + switch (base.cv_qualifier()) + { + case cpp_cv_none: + break; + case cpp_cv_const: + output << keyword("const"); + need_ws = true; + break; + case cpp_cv_volatile: + output << keyword("volatile"); + need_ws = true; + break; + case cpp_cv_const_volatile: + output << keyword("const") << whitespace << keyword("volatile"); + need_ws = true; + break; + } + + switch (base.ref_qualifier()) + { + case cpp_ref_none: + break; + case cpp_ref_lvalue: + output << punctuation("&"); + need_ws = false; + break; + case cpp_ref_rvalue: + output << punctuation("&&"); + need_ws = false; + break; + } + + return need_ws; + } + + void generate_member_function( + code_generator& generator, const cpp_member_function& func, + type_safe::optional_ref spec = nullptr) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true); + if (output) + { + if (func.is_constexpr()) + output << keyword("constexpr") << whitespace; + else + write_prefix_virtual(output, func.virtual_info()); + + if (detail::is_complex_type(func.return_type())) + output << keyword("auto") << whitespace; + else + { + detail::write_type(output, func.return_type(), ""); + output << whitespace; + } + + if (spec) + { + output << spec.value().primary_template(); + write_specialization_arguments(output, spec.value()); + } + else + output << identifier(func.name()); + write_function_parameters(output, func); + auto need_ws = write_cv_ref(output, func); + write_noexcept(output, func, need_ws); + + if (detail::is_complex_type(func.return_type())) + { + output << punctuation("->"); + detail::write_type(output, func.return_type(), ""); + } + + write_suffix_virtual(output, func.virtual_info()); + write_function_body(output, func, is_pure(func.virtual_info())); + } + } + + void generate_conversion_op(code_generator& generator, const cpp_conversion_op& op) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(op), true); + if (output) + { + if (op.is_explicit()) + output << keyword("explicit") << whitespace; + if (op.is_constexpr()) + output << keyword("constexpr") << whitespace; + else + write_prefix_virtual(output, op.virtual_info()); + + auto pos = op.name().find("operator"); + output << identifier(op.name().substr(0u, pos)) << keyword("operator") << whitespace; + detail::write_type(output, op.return_type(), ""); + output << punctuation("(") << punctuation(")"); + auto need_ws = write_cv_ref(output, op); + write_noexcept(output, op, need_ws); + + write_suffix_virtual(output, op.virtual_info()); + write_function_body(output, op, is_pure(op.virtual_info())); + } + } + + void generate_constructor(code_generator& generator, const cpp_constructor& ctor) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(ctor), true); + if (output) + { + if (ctor.is_explicit()) + output << keyword("explicit") << whitespace; + if (ctor.is_constexpr()) + output << keyword("constexpr") << whitespace; + + output << identifier(ctor.name()); + write_function_parameters(output, ctor); + write_noexcept(output, ctor, false); + + write_function_body(output, ctor, false); + } + } + + void generate_destructor(code_generator& generator, const cpp_destructor& dtor) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(dtor), true); + if (output) + { + write_prefix_virtual(output, dtor.virtual_info()); + output << identifier(dtor.name()) << punctuation("(") << punctuation(")"); + write_noexcept(output, dtor, false); + + write_suffix_virtual(output, dtor.virtual_info()); + write_function_body(output, dtor, is_pure(dtor.virtual_info())); + } + } + + void generate_function_base(code_generator& generator, const cpp_function_base& base, + const cpp_template_specialization& spec) + { + switch (base.kind()) + { + case cpp_entity_kind::function_t: + generate_function(generator, static_cast(base), + type_safe::ref(spec)); + break; + case cpp_entity_kind::member_function_t: + generate_member_function(generator, static_cast(base), + type_safe::ref(spec)); + break; + case cpp_entity_kind::conversion_op_t: + generate_conversion_op(generator, static_cast(base)); + break; + case cpp_entity_kind::constructor_t: + generate_constructor(generator, static_cast(base)); + break; + + default: + DEBUG_UNREACHABLE(detail::assert_handler{}); + break; + } + } + + void generate_template_type_parameter(code_generator& generator, + const cpp_template_type_parameter& param) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(param), false); + if (output) + { + output << keyword(to_string(param.keyword())); + if (param.is_variadic()) + output << punctuation("..."); + if (!param.name().empty()) + output << whitespace << identifier(param.name()); + if (param.default_type()) + { + output << punctuation("="); + detail::write_type(output, param.default_type().value(), ""); + } + } + } + + void generate_non_type_template_parameter(code_generator& generator, + const cpp_non_type_template_parameter& param) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(param), false); + if (output) + { + detail::write_type(output, param.type(), param.name(), param.is_variadic()); + if (param.default_value()) + { + output << punctuation("="); + detail::write_expression(output, param.default_value().value()); + } + } + } + + void generate_template_template_parameter(code_generator& generator, + const cpp_template_template_parameter& param) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(param), true); + if (output) + { + output << keyword("template") << punctuation("<"); + write_container(output, param, punctuation(",")); + output << punctuation(">") << keyword(to_string(param.keyword())) << whitespace; + if (param.is_variadic()) + output << punctuation("..."); + output << identifier(param.name()); + if (param.default_template()) + output << punctuation("=") << param.default_template().value(); + } + } + + void write_template_parameters(code_generator::output& output, const cpp_template& templ) + { + output << keyword("template") << punctuation("<"); + auto need_sep = false; + for (auto& param : templ.parameters()) + { + if (need_sep) + output << punctuation(","); + else + need_sep = true; + generate_code(*output.generator(), param); + } + output << punctuation(">") << newl; + } + + void generate_alias_template(code_generator& generator, const cpp_alias_template& alias) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), true); + if (output) + { + write_template_parameters(output, alias); + generate_code(generator, alias.type_alias()); + } + } + + void generate_variable_template(code_generator& generator, const cpp_variable_template& var) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(var), true); + if (output) + { + write_template_parameters(output, var); + generate_code(generator, var.variable()); + } + } + + void generate_function_template(code_generator& generator, const cpp_function_template& func) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true); + if (output) + { + write_template_parameters(output, func); + generate_code(generator, func.function()); + } + } + + void generate_function_template_specialization(code_generator& generator, + const cpp_function_template_specialization& func) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true); + if (output) + { + write_template_parameters(output, func); + generate_function_base(generator, func.function(), func); + } + } + + void generate_class_template(code_generator& generator, const cpp_class_template& templ) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), true); + if (output) + { + write_template_parameters(output, templ); + generate_class(generator, templ.class_()); + } + } + + void generate_class_template_specialization(code_generator& generator, + const cpp_class_template_specialization& templ) + { + code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), true); + if (output) + { + write_template_parameters(output, templ); + generate_class(generator, templ.class_(), type_safe::ref(templ)); + } + } +} + +void cppast::generate_code(code_generator& generator, const cpp_entity& e) +{ + switch (e.kind()) + { +#define CPPAST_DETAIL_HANDLE(Name) \ + case cpp_entity_kind::Name##_t: \ + generate_##Name(generator, static_cast(e)); \ + break; + + CPPAST_DETAIL_HANDLE(file) + + CPPAST_DETAIL_HANDLE(macro_definition) + CPPAST_DETAIL_HANDLE(include_directive) + + CPPAST_DETAIL_HANDLE(language_linkage) + CPPAST_DETAIL_HANDLE(namespace) + CPPAST_DETAIL_HANDLE(namespace_alias) + CPPAST_DETAIL_HANDLE(using_directive) + CPPAST_DETAIL_HANDLE(using_declaration) + + CPPAST_DETAIL_HANDLE(type_alias) + + CPPAST_DETAIL_HANDLE(enum) + CPPAST_DETAIL_HANDLE(enum_value) + + CPPAST_DETAIL_HANDLE(class) + CPPAST_DETAIL_HANDLE(access_specifier) + CPPAST_DETAIL_HANDLE(base_class) + + CPPAST_DETAIL_HANDLE(variable) + CPPAST_DETAIL_HANDLE(member_variable) + CPPAST_DETAIL_HANDLE(bitfield) + + CPPAST_DETAIL_HANDLE(function_parameter) + CPPAST_DETAIL_HANDLE(function) + CPPAST_DETAIL_HANDLE(member_function) + CPPAST_DETAIL_HANDLE(conversion_op) + CPPAST_DETAIL_HANDLE(constructor) + CPPAST_DETAIL_HANDLE(destructor) + + CPPAST_DETAIL_HANDLE(template_type_parameter) + CPPAST_DETAIL_HANDLE(non_type_template_parameter) + CPPAST_DETAIL_HANDLE(template_template_parameter) + + CPPAST_DETAIL_HANDLE(alias_template) + CPPAST_DETAIL_HANDLE(variable_template) + CPPAST_DETAIL_HANDLE(function_template) + CPPAST_DETAIL_HANDLE(function_template_specialization) + CPPAST_DETAIL_HANDLE(class_template) + CPPAST_DETAIL_HANDLE(class_template_specialization) + +#undef CPPAST_DETAIL_HANDLE + + case cpp_entity_kind::count: + DEBUG_UNREACHABLE(detail::assert_handler{}); + break; + } +} + +void detail::write_template_arguments(code_generator::output& output, + type_safe::array_ref arguments) +{ + if (arguments.size() == 0u) + return; + + output << punctuation("<"); + auto need_sep = false; + for (auto& arg : arguments) + { + if (need_sep) + output << punctuation(","); + else + need_sep = true; + + if (auto type = arg.type()) + detail::write_type(output, type.value(), ""); + else if (auto expr = arg.expression()) + detail::write_expression(output, expr.value()); + else if (auto templ = arg.template_ref()) + output << templ.value(); + else + DEBUG_UNREACHABLE(detail::assert_handler{}); + } + output << punctuation(">"); +} diff --git a/src/cpp_expression.cpp b/src/cpp_expression.cpp new file mode 100644 index 0000000..f0142cd --- /dev/null +++ b/src/cpp_expression.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#include + +using namespace cppast; + +namespace +{ + void write_literal(code_generator::output& output, const cpp_literal_expression& expr) + { + auto type_kind = cpp_void; + if (expr.type().kind() == cpp_type_kind::builtin_t) + type_kind = static_cast(expr.type()).builtin_type_kind(); + else if (expr.type().kind() == cpp_type_kind::pointer_t) + { + auto& pointee = static_cast(expr.type()).pointee(); + if (pointee.kind() == cpp_type_kind::builtin_t) + { + auto& builtin_pointee = static_cast(pointee); + if (builtin_pointee.builtin_type_kind() == cpp_char + || builtin_pointee.builtin_type_kind() == cpp_wchar + || builtin_pointee.builtin_type_kind() == cpp_char16 + || builtin_pointee.builtin_type_kind() == cpp_char32) + // pointer to char aka string + type_kind = builtin_pointee.builtin_type_kind(); + } + } + + switch (type_kind) + { + case cpp_void: + output << token_seq(expr.value()); + break; + + case cpp_bool: + output << keyword(expr.value()); + break; + + case cpp_uchar: + case cpp_ushort: + case cpp_uint: + case cpp_ulong: + case cpp_ulonglong: + case cpp_uint128: + case cpp_schar: + case cpp_short: + case cpp_int: + case cpp_long: + case cpp_longlong: + case cpp_int128: + output << int_literal(expr.value()); + break; + + case cpp_float: + case cpp_double: + case cpp_longdouble: + case cpp_float128: + output << float_literal(expr.value()); + break; + + case cpp_char: + case cpp_wchar: + case cpp_char16: + case cpp_char32: + output << string_literal(expr.value()); + break; + + case cpp_nullptr: + output << keyword(expr.value()); + break; + } + } + + void write_unexposed(code_generator::output& output, const cpp_unexposed_expression& expr) + { + output << token_seq(expr.expression()); + } +} + +void detail::write_expression(code_generator::output& output, const cpp_expression& expr) +{ + switch (expr.kind()) + { + case cpp_expression_kind::literal_t: + write_literal(output, static_cast(expr)); + break; + case cpp_expression_kind::unexposed_t: + write_unexposed(output, static_cast(expr)); + break; + } +} diff --git a/src/cpp_type.cpp b/src/cpp_type.cpp index 19d955e..1f55d3d 100644 --- a/src/cpp_type.cpp +++ b/src/cpp_type.cpp @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -91,3 +92,382 @@ std::unique_ptr cpp_dependent_type::build( return std::unique_ptr( new cpp_dependent_type(std::move(name), std::move(dependee))); } + +namespace +{ + // is directly a complex type + // is_complex_type also checks for children + bool is_direct_complex(const cpp_type& type) noexcept + { + switch (type.kind()) + { + case cpp_type_kind::builtin_t: + case cpp_type_kind::user_defined_t: + case cpp_type_kind::auto_t: + case cpp_type_kind::decltype_t: + case cpp_type_kind::decltype_auto_t: + case cpp_type_kind::cv_qualified_t: + case cpp_type_kind::pointer_t: + case cpp_type_kind::reference_t: + case cpp_type_kind::template_parameter_t: + case cpp_type_kind::template_instantiation_t: + case cpp_type_kind::dependent_t: + case cpp_type_kind::unexposed_t: + return false; + + case cpp_type_kind::array_t: + case cpp_type_kind::function_t: + case cpp_type_kind::member_function_t: + case cpp_type_kind::member_object_t: + return true; + } + + DEBUG_UNREACHABLE(detail::assert_handler{}); + return false; + } +} + +bool detail::is_complex_type(const cpp_type& type) noexcept +{ + switch (type.kind()) + { + case cpp_type_kind::cv_qualified_t: + return is_complex_type(static_cast(type).type()); + case cpp_type_kind::pointer_t: + return is_complex_type(static_cast(type).pointee()); + case cpp_type_kind::reference_t: + return is_complex_type(static_cast(type).referee()); + + default: + break; + } + + return is_direct_complex(type); +} + +namespace +{ + void write_builtin(code_generator::output& output, const cpp_builtin_type& type) + { + output << keyword(to_string(type.builtin_type_kind())); + } + + void write_user_defined(code_generator::output& output, const cpp_user_defined_type& type) + { + output << type.entity(); + } + + void write_auto(code_generator::output& output, const cpp_auto_type&) + { + output << keyword("auto"); + } + + void write_decltype(code_generator::output& output, const cpp_decltype_type& type) + { + output << keyword("decltype") << punctuation("("); + detail::write_expression(output, type.expression()); + output << punctuation(")"); + } + + void write_decltype_auto(code_generator::output& output, const cpp_decltype_auto_type&) + { + output << keyword("decltype") << punctuation("(") << keyword("auto") << punctuation(")"); + } + + void write_cv_qualified_prefix(code_generator::output& output, + const cpp_cv_qualified_type& type) + { + detail::write_type_prefix(output, type.type()); + + if (is_direct_complex(type.type())) + output << punctuation("("); + + if (is_const(type.cv_qualifier())) + output << whitespace << keyword("const"); + if (is_volatile(type.cv_qualifier())) + output << whitespace << keyword("volatile"); + } + + void write_cv_qualified_suffix(code_generator::output& output, + const cpp_cv_qualified_type& type) + { + if (is_direct_complex(type.type())) + output << punctuation(")"); + detail::write_type_suffix(output, type.type()); + } + + bool pointer_requires_paren(const cpp_pointer_type& type) + { + auto kind = type.pointee().kind(); + return kind == cpp_type_kind::function_t || kind == cpp_type_kind::array_t; + } + + void write_pointer_prefix(code_generator::output& output, const cpp_pointer_type& type) + { + detail::write_type_prefix(output, type.pointee()); + if (pointer_requires_paren(type)) + output << punctuation("("); + + output << punctuation("*"); + } + + void write_pointer_suffix(code_generator::output& output, const cpp_pointer_type& type) + { + if (pointer_requires_paren(type)) + output << punctuation(")"); + detail::write_type_suffix(output, type.pointee()); + } + + void write_reference_prefix(code_generator::output& output, const cpp_reference_type& type) + { + detail::write_type_prefix(output, type.referee()); + if (is_direct_complex(type.referee())) + output << punctuation("("); + + if (type.reference_kind() == cpp_ref_lvalue) + output << punctuation("&"); + else if (type.reference_kind() == cpp_ref_rvalue) + output << punctuation("&&"); + else + DEBUG_UNREACHABLE(detail::assert_handler{}); + } + + void write_reference_suffix(code_generator::output& output, const cpp_reference_type& type) + { + if (is_direct_complex(type.referee())) + output << punctuation(")"); + detail::write_type_suffix(output, type.referee()); + } + + void write_array_prefix(code_generator::output& output, const cpp_array_type& type) + { + detail::write_type_prefix(output, type.value_type()); + } + + void write_array_suffix(code_generator::output& output, const cpp_array_type& type) + { + output << punctuation("["); + if (type.size()) + detail::write_expression(output, type.size().value()); + output << punctuation("]"); + detail::write_type_suffix(output, type.value_type()); + } + + void write_function_prefix(code_generator::output& output, const cpp_function_type& type) + { + detail::write_type_prefix(output, type.return_type()); + } + + template + void write_parameters(code_generator::output& output, const T& type) + { + output << punctuation("("); + + auto need_sep = false; + for (auto& param : type.parameter_types()) + { + if (need_sep) + output << punctuation(","); + else + need_sep = true; + detail::write_type_prefix(output, param); + detail::write_type_suffix(output, param); + } + if (type.is_variadic()) + { + if (need_sep) + output << punctuation(","); + output << punctuation("..."); + } + + output << punctuation(")"); + } + + void write_function_suffix(code_generator::output& output, const cpp_function_type& type) + { + write_parameters(output, type); + + detail::write_type_suffix(output, type.return_type()); + } + + const cpp_type& strip_class_type(const cpp_type& type, cpp_cv* cv, cpp_reference* ref) + { + if (type.kind() == cpp_type_kind::cv_qualified_t) + { + auto& cv_qual = static_cast(type); + if (cv) + *cv = cv_qual.cv_qualifier(); + return strip_class_type(cv_qual.type(), cv, ref); + } + else if (type.kind() == cpp_type_kind::reference_t) + { + auto& ref_type = static_cast(type); + if (ref) + *ref = ref_type.reference_kind(); + return strip_class_type(ref_type.referee(), cv, ref); + } + else + { + DEBUG_ASSERT(!detail::is_complex_type(type), detail::assert_handler{}); + return type; + } + } + + void write_member_function_prefix(code_generator::output& output, + const cpp_member_function_type& type) + { + detail::write_type_prefix(output, type.return_type()); + + output << punctuation("("); + detail::write_type_prefix(output, strip_class_type(type.class_type(), nullptr, nullptr)); + output << punctuation("::"); + } + + void write_member_function_suffix(code_generator::output& output, + const cpp_member_function_type& type) + { + output << punctuation(")"); + write_parameters(output, type); + + auto cv = cpp_cv_none; + auto ref = cpp_ref_none; + strip_class_type(type.class_type(), &cv, &ref); + + if (cv == cpp_cv_const_volatile) + output << keyword("const") << whitespace << keyword("volatile"); + else if (is_const(cv)) + output << keyword("const"); + else if (is_volatile(cv)) + output << keyword("volatile"); + + if (ref == cpp_ref_lvalue) + output << punctuation("&"); + else if (ref == cpp_ref_rvalue) + output << punctuation("&&"); + + detail::write_type_suffix(output, type.return_type()); + } + + void write_member_object_prefix(code_generator::output& output, + const cpp_member_object_type& type) + { + detail::write_type_prefix(output, type.object_type()); + output << punctuation("("); + DEBUG_ASSERT(!detail::is_complex_type(type.class_type()), detail::assert_handler{}); + detail::write_type_prefix(output, type.class_type()); + output << punctuation("::"); + } + + void write_member_object_suffix(code_generator::output& output, const cpp_member_object_type&) + { + output << punctuation(")"); + } + + void write_template_parameter(code_generator::output& output, + const cpp_template_parameter_type& type) + { + output << type.entity(); + } + + void write_template_instantiation(code_generator::output& output, + const cpp_template_instantiation_type& type) + { + output << type.primary_template(); + if (type.arguments_exposed()) + detail::write_template_arguments(output, type.arguments()); + else + output << punctuation("<") << token_seq(type.unexposed_arguments()) << punctuation(">"); + } + + void write_dependent(code_generator::output& output, const cpp_dependent_type& type) + { + output << token_seq(type.name()); + } + + void write_unexposed(code_generator::output& output, const cpp_unexposed_type& type) + { + output << token_seq(type.name()); + } +} + +void detail::write_type_prefix(code_generator::output& output, const cpp_type& type) +{ + switch (type.kind()) + { +#define CPPAST_DETAIL_HANDLE(Name) \ + case cpp_type_kind::Name##_t: \ + write_##Name(output, static_cast(type)); \ + break; + +#define CPPAST_DETAIL_HANDLE_COMPLEX(Name) \ + case cpp_type_kind::Name##_t: \ + write_##Name##_prefix(output, static_cast(type)); \ + break; + + CPPAST_DETAIL_HANDLE(builtin) + CPPAST_DETAIL_HANDLE(user_defined) + CPPAST_DETAIL_HANDLE(auto) + CPPAST_DETAIL_HANDLE(decltype) + CPPAST_DETAIL_HANDLE(decltype_auto) + CPPAST_DETAIL_HANDLE_COMPLEX(cv_qualified) + CPPAST_DETAIL_HANDLE_COMPLEX(pointer) + CPPAST_DETAIL_HANDLE_COMPLEX(reference) + CPPAST_DETAIL_HANDLE_COMPLEX(array) + CPPAST_DETAIL_HANDLE_COMPLEX(function) + CPPAST_DETAIL_HANDLE_COMPLEX(member_function) + CPPAST_DETAIL_HANDLE_COMPLEX(member_object) + CPPAST_DETAIL_HANDLE(template_parameter) + CPPAST_DETAIL_HANDLE(template_instantiation) + CPPAST_DETAIL_HANDLE(dependent) + CPPAST_DETAIL_HANDLE(unexposed) + } + +#undef CPPAST_DETAIL_HANDLE +#undef CPPAST_DETAIL_HANDLE_COMPLEX +} + +void detail::write_type_suffix(code_generator::output& output, const cpp_type& type) +{ + switch (type.kind()) + { +#define CPPAST_DETAIL_HANDLE(Name) \ + case cpp_type_kind::Name##_t: \ + break; + +#define CPPAST_DETAIL_HANDLE_COMPLEX(Name) \ + case cpp_type_kind::Name##_t: \ + write_##Name##_suffix(output, static_cast(type)); \ + break; + + CPPAST_DETAIL_HANDLE(builtin) + CPPAST_DETAIL_HANDLE(user_defined) + CPPAST_DETAIL_HANDLE(auto) + CPPAST_DETAIL_HANDLE(decltype) + CPPAST_DETAIL_HANDLE(decltype_auto) + CPPAST_DETAIL_HANDLE_COMPLEX(cv_qualified) + CPPAST_DETAIL_HANDLE_COMPLEX(pointer) + CPPAST_DETAIL_HANDLE_COMPLEX(reference) + CPPAST_DETAIL_HANDLE_COMPLEX(array) + CPPAST_DETAIL_HANDLE_COMPLEX(function) + CPPAST_DETAIL_HANDLE_COMPLEX(member_function) + CPPAST_DETAIL_HANDLE_COMPLEX(member_object) + CPPAST_DETAIL_HANDLE(template_parameter) + CPPAST_DETAIL_HANDLE(template_instantiation) + CPPAST_DETAIL_HANDLE(dependent) + CPPAST_DETAIL_HANDLE(unexposed) + } + +#undef CPPAST_DETAIL_HANDLE +#undef CPPAST_DETAIL_HANDLE_COMPLEX +} + +void detail::write_type(code_generator::output& output, const cpp_type& type, std::string name, + bool is_variadic) +{ + write_type_prefix(output, type); + if (!name.empty()) + output << whitespace << identifier(name); + if (is_variadic) + output << punctuation("..."); + write_type_suffix(output, type); +} diff --git a/src/libclang/function_parser.cpp b/src/libclang/function_parser.cpp index 4588252..78bf359 100644 --- a/src/libclang/function_parser.cpp +++ b/src/libclang/function_parser.cpp @@ -17,7 +17,7 @@ namespace { auto name = detail::get_cursor_name(cur); auto type = detail::parse_type(context, cur, clang_getCursorType(cur)); - auto is_decltype = type->kind() == cpp_type_kind::decltype_; + auto is_decltype = type->kind() == cpp_type_kind::decltype_t; std::unique_ptr default_value; detail::visit_children(cur, [&](const CXCursor& child) { diff --git a/src/libclang/preprocessor.cpp b/src/libclang/preprocessor.cpp index 131fff0..c1310f5 100644 --- a/src/libclang/preprocessor.cpp +++ b/src/libclang/preprocessor.cpp @@ -299,13 +299,13 @@ namespace return result; } - bool bump_c_comment(position& p, detail::preprocessor_output& output) + bool bump_c_comment(position& p, detail::preprocessor_output& output, bool in_main_file) { if (!starts_with(p, "/*")) return false; p.bump(2u); - if (starts_with(p, "*")) + if (in_main_file && starts_with(p, "*")) { // doc comment p.bump(); @@ -371,20 +371,20 @@ namespace } } - bool bump_cpp_comment(position& p, detail::preprocessor_output& output) + bool bump_cpp_comment(position& p, detail::preprocessor_output& output, bool in_main_file) { if (!starts_with(p, "//")) return false; p.bump(2u); - if (starts_with(p, "/") || starts_with(p, "!")) + if (in_main_file && (starts_with(p, "/") || starts_with(p, "!"))) { // C++ style doc comment p.bump(); auto comment = parse_cpp_doc_comment(p, false); merge_or_add(output, std::move(comment)); } - else if (starts_with(p, "<")) + else if (in_main_file && starts_with(p, "<")) { // end of line doc comment p.bump(); @@ -408,7 +408,8 @@ namespace } std::unique_ptr parse_macro(position& p, - detail::preprocessor_output& output) + detail::preprocessor_output& output, + bool in_main_file) { // format (at new line): #define [replacement] // or: #define () [replacement] @@ -446,6 +447,9 @@ namespace } // don't skip newline + if (!in_main_file) + return nullptr; + auto result = cpp_macro_definition::build(std::move(name), std::move(args), std::move(rep)); // match comment directly if (!output.comments.empty() && output.comments.back().matches(*result, p.cur_line())) @@ -472,7 +476,8 @@ namespace } std::unique_ptr parse_include(position& p, - detail::preprocessor_output& output) + detail::preprocessor_output& output, + bool in_main_file) { // format (at new line, literal <>): #include // or: #include "filename" @@ -505,6 +510,9 @@ namespace DEBUG_ASSERT(starts_with(p, "\n"), detail::assert_handler{}); // don't skip newline + if (!in_main_file) + return nullptr; + auto result = cpp_include_directive::build(cpp_file_ref(cpp_entity_id(filename), filename), include_kind); if (!output.comments.empty() && output.comments.back().matches(*result, p.cur_line())) @@ -614,10 +622,9 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co std::size_t file_depth = 0u; while (p) { - if (auto macro = parse_macro(p, result)) + if (auto macro = parse_macro(p, result, file_depth == 0u)) { - if (file_depth == 0u) - result.entities.push_back({std::move(macro), p.cur_line()}); + result.entities.push_back({std::move(macro), p.cur_line()}); } else if (auto undef = parse_undef(p)) { @@ -631,7 +638,7 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co }), result.entities.end()); } - else if (auto include = parse_include(p, result)) + else if (auto include = parse_include(p, result, file_depth == 0u)) { if (file_depth == 0u) result.entities.push_back({std::move(include), p.cur_line()}); @@ -669,9 +676,9 @@ detail::preprocessor_output detail::preprocess(const libclang_compile_config& co break; } } - else if (bump_c_comment(p, result)) + else if (bump_c_comment(p, result, file_depth == 0u)) continue; - else if (bump_cpp_comment(p, result)) + else if (bump_cpp_comment(p, result, file_depth == 0u)) continue; else p.bump(); diff --git a/test/code_generator.cpp b/test/code_generator.cpp new file mode 100644 index 0000000..dc85be4 --- /dev/null +++ b/test/code_generator.cpp @@ -0,0 +1,39 @@ +// Copyright (C) 2017 Jonathan Müller +// This file is subject to the license terms in the LICENSE file +// found in the top-level directory of this distribution. + +#include + +#include "test_parser.hpp" + +TEST_CASE("code_generator") +{ + // no need to check much here, as each entity check separately + // only write some file with equivalent code and synopsis + auto code = R"(using type=int; + +struct foo{ + int a; + + auto func(int)->int(*)[]; + +private: + int const b=42; +}; + +int(*(foo::* mptr)(int))[]; + +enum class bar +:int{ + a, + b=42 +}; + +void func(int(*)(int)); + +extern void(* ptr)(int(*)(int))=&func; +)"; + + auto file = parse({}, "code_generator.cpp", code); + REQUIRE(get_code(*file) == code); +} diff --git a/test/cpp_alias_template.cpp b/test/cpp_alias_template.cpp index e88cb6f..b836d12 100644 --- a/test/cpp_alias_template.cpp +++ b/test/cpp_alias_template.cpp @@ -12,27 +12,43 @@ TEST_CASE("cpp_alias_template") { // no need to check advanced types here nor template parameters auto code = R"( +/// template +/// using a=int; template using a = int; +/// template +/// using b=T; template using b = T; +/// template +/// using c=T const*; template using c = const T*; +/// template +/// using d=a; template using d = a; +/// template +/// using e=b const; template using e = const b; +/// template +/// using f=b{(0 , 1)}, int>; template using f = b{(0,1)}, int>; +/// templateclass Templ> +/// using g=Templ; template class Templ> using g = Templ; +/// template +/// using h=g; template using h = g; )"; diff --git a/test/cpp_class.cpp b/test/cpp_class.cpp index 5b2f6ab..1935141 100644 --- a/test/cpp_class.cpp +++ b/test/cpp_class.cpp @@ -12,16 +12,40 @@ TEST_CASE("cpp_class") { auto code = R"( // forward declarations +/// struct a; struct a; +/// class b; class b; +/// struct unresolved; struct unresolved; // basic +/// struct a{ +/// }; struct a {}; +/// class b final{ +/// }; class b final {}; +/// union c{ +/// }; union c {}; // members +/// struct d{ +/// enum m1{ +/// }; +/// +/// enum m2{ +/// }; +/// +/// private: +/// enum m3{ +/// }; +/// +/// protected: +/// enum m4{ +/// }; +/// }; struct d { enum m1 {}; @@ -38,20 +62,31 @@ protected: }; // bases +/// class e +/// :a,d{ +/// }; class e : a, private d {}; namespace ns { + /// struct base{ + /// }; struct base {}; } +/// struct f +/// :ns::base,virtual protected e{ +/// }; struct f : public ns::base, virtual protected e {}; using namespace ns; +/// struct g +/// :base{ +/// }; struct g : base {}; )"; diff --git a/test/cpp_class_template.cpp b/test/cpp_class_template.cpp index 21b87d0..44f24ea 100644 --- a/test/cpp_class_template.cpp +++ b/test/cpp_class_template.cpp @@ -16,20 +16,45 @@ TEST_CASE("cpp_class_template") { auto code = R"( // check everything not related to members first +/// template +/// class a{ +/// }; template class a {}; +/// template +/// struct b{ +/// }; template struct b {}; +/// templateclass T> +/// union c; template