Add ability to format generated code

This commit is contained in:
Jonathan Müller 2017-05-27 22:47:47 +02:00
commit 6023f293df
4 changed files with 293 additions and 165 deletions

View file

@ -119,6 +119,27 @@ namespace cppast
{
} whitespace{};
/// Flags that control the code formatting.
///
/// If a flag is set, it adds additional whitespace.
/// If no flags are set, it will only add the whitespace necessary to separate tokens.
enum class formatting_flags
{
brace_nl, //< Set to put the opening braces on a new line.
brace_ws, //< Set to put the opening brace at the end of the line after whitespace.
ptr_ref_var, //< Set to put pointers and references at the variable name (default is type).
comma_ws, //< Set to put whitespace after a comma.
bracket_ws, //< Set to put whitespace inside brackets.
operator_ws, //< Set to put whitespace around operators.
_flag_set_size, //< \exclude
};
/// A set of formatting flags.
using formatting = type_safe::flag_set<formatting_flags>;
/// Base class to control the code generation.
///
/// Inherit from it to customize how a [cppast::cpp_entity]() is printed
@ -127,8 +148,10 @@ namespace cppast
{
public:
code_generator(const code_generator&) = delete;
code_generator& operator=(const code_generator&) = delete;
virtual ~code_generator() noexcept = default;
virtual ~code_generator() noexcept = default;
/// Flags that control the generation.
enum generation_flags
@ -147,37 +170,22 @@ namespace cppast
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.
/// \effects Creates it giving the generator and the entity.
explicit output(type_safe::object_ref<code_generator> gen,
type_safe::object_ref<const cpp_entity> e, bool is_container)
: gen_(gen), options_(gen->do_get_options(*e))
type_safe::object_ref<const cpp_entity> e)
: gen_(gen), e_(e), options_(gen->do_get_options(*e))
{
if (is_container)
{
gen_->on_container_begin(*e);
e_ = e;
}
else
gen_->on_leaf(*e);
gen_->on_begin(*e_);
}
/// \effects If the entity is a container
/// and `on_container_begin()` returned `true`,
/// calls `on_container_end()`,
/// else does nothing.
~output() noexcept
{
if (*this && e_)
gen_->on_container_end(e_.value());
if (*this)
gen_->on_end(*e_);
}
output(const output&) = delete;
output& operator=(const output&) = delete;
/// \returns Whether or not the `on_XXX` function returned something other than `exclude`.
@ -189,17 +197,23 @@ namespace cppast
}
/// \returns The generation options.
generation_options options() const noexcept
generation_options options() const
{
return options_;
}
/// \returns The generation options for the given entity.
generation_options options(const cpp_entity& e) const noexcept
generation_options options(const cpp_entity& e) const
{
return gen_->do_get_options(e);
}
/// \returns The formatting.
cppast::formatting formatting() const
{
return gen_->do_get_formatting();
}
/// \returns Whether or not the definition should be generated as well.
bool generate_definition() const noexcept
{
@ -230,6 +244,13 @@ namespace cppast
gen_->do_unindent();
}
/// \effects Calls `func(*this)`.
const output& operator<<(void (*func)(const output&)) const
{
func(*this);
return *this;
}
/// \effects Calls `do_write_keyword()`.
const output& operator<<(const keyword& k) const
{
@ -328,15 +349,22 @@ namespace cppast
}
private:
type_safe::object_ref<code_generator> gen_;
type_safe::optional_ref<const cpp_entity> e_;
generation_options options_;
type_safe::object_ref<code_generator> gen_;
type_safe::object_ref<const cpp_entity> e_;
generation_options options_;
};
protected:
code_generator() noexcept = default;
private:
/// \returns The formatting options that should be used.
/// The base class version has no flags set.
virtual formatting do_get_formatting() const
{
return {};
}
/// \returns The generation options for that entity.
/// The base class version always returns no special options.
virtual generation_options do_get_options(const cpp_entity& e)
@ -345,23 +373,16 @@ namespace cppast
return {};
}
/// \effects Will be invoked before code of a container entity is generated.
/// \effects Will be invoked before code of an entity is generated.
/// The base class version has no effect.
virtual void on_container_begin(const cpp_entity& e)
virtual void on_begin(const cpp_entity& e)
{
(void)e;
}
/// \effects Will be invoked after all code of a container entity has been generated.
/// \effects Will be invoked after all code of an 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.
virtual void on_leaf(const cpp_entity& e)
virtual void on_end(const cpp_entity& e)
{
(void)e;
}
@ -451,9 +472,6 @@ namespace cppast
/// 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.
///
/// \returns Whether or not any code was actually written.
bool generate_code(code_generator& generator, const cpp_entity& e);

View file

@ -28,16 +28,32 @@ using namespace cppast;
namespace
{
template <typename Sep>
auto write_sep(code_generator::output& output, Sep s) -> decltype(output << s)
void opening_brace(const code_generator::output& output)
{
return output << s;
if (output.formatting().is_set(formatting_flags::brace_nl))
output << newl;
else if (output.formatting().is_set(formatting_flags::brace_ws))
output << whitespace;
output << punctuation("{");
}
template <typename Sep>
auto write_sep(code_generator::output& output, Sep s) -> decltype(s(output))
void comma(const code_generator::output& output)
{
return s(output);
output << punctuation(",");
if (output.formatting().is_set(formatting_flags::comma_ws))
output << whitespace;
}
void bracket_ws(const code_generator::output& output)
{
if (output.formatting().is_set(formatting_flags::bracket_ws))
output << whitespace;
}
void operator_ws(const code_generator::output& output)
{
if (output.formatting().is_set(formatting_flags::operator_ws))
output << whitespace;
}
template <class Container, typename Sep>
@ -47,7 +63,7 @@ namespace
for (auto& child : cont)
{
if (need_sep)
write_sep(output, s);
output << s;
need_sep = generate_code(*output.generator(), child);
}
return need_sep;
@ -55,7 +71,7 @@ namespace
bool generate_file(code_generator& generator, const cpp_file& f)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(f), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(f));
if (output)
{
auto need_sep = write_container(output, f, newl);
@ -68,12 +84,13 @@ namespace
bool generate_macro_definition(code_generator& generator, const cpp_macro_definition& def)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(def), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(def));
if (output)
{
output << preprocessor_token("#define") << whitespace << identifier(def.name());
if (def.is_function_like())
output << preprocessor_token("(") << preprocessor_token(def.parameters().value())
output << preprocessor_token("(") << bracket_ws
<< preprocessor_token(def.parameters().value()) << bracket_ws
<< preprocessor_token(")");
if (!def.replacement().empty())
output << whitespace << preprocessor_token(def.replacement()) << newl;
@ -85,7 +102,7 @@ namespace
bool generate_include_directive(code_generator& generator, const cpp_include_directive& include)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(include), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(include));
if (output)
{
output << preprocessor_token("#include") << whitespace;
@ -105,13 +122,13 @@ namespace
bool generate_language_linkage(code_generator& generator, const cpp_language_linkage& linkage)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(linkage), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(linkage));
if (output)
{
output << keyword("extern") << whitespace << string_literal(linkage.name());
if (linkage.is_block())
{
output << punctuation("{");
output << opening_brace;
output.indent();
write_container(output, linkage, newl);
@ -130,13 +147,13 @@ namespace
bool generate_namespace(code_generator& generator, const cpp_namespace& ns)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(ns), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(ns));
if (output)
{
if (ns.is_inline())
output << keyword("inline") << whitespace;
output << keyword("namespace") << whitespace << identifier(ns.name());
output << punctuation("{");
output << opening_brace;
output.indent();
write_container(output, ns, newl);
@ -149,11 +166,11 @@ namespace
bool generate_namespace_alias(code_generator& generator, const cpp_namespace_alias& alias)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias));
if (output)
{
output << keyword("namespace") << whitespace << identifier(alias.name())
<< punctuation("=");
output << keyword("namespace") << whitespace << identifier(alias.name()) << operator_ws
<< punctuation("=") << operator_ws;
if (output.options() & code_generator::exclude_target)
output.excluded(alias);
else
@ -165,7 +182,7 @@ namespace
bool generate_using_directive(code_generator& generator, const cpp_using_directive& directive)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(directive), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(directive));
if (output)
output << keyword("using") << whitespace << keyword("namespace") << whitespace
<< directive.target() << punctuation(";") << newl;
@ -175,8 +192,7 @@ namespace
bool generate_using_declaration(code_generator& generator,
const cpp_using_declaration& declaration)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(declaration),
false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(declaration));
if (output)
output << keyword("using") << whitespace << declaration.target() << punctuation(";")
<< newl;
@ -185,11 +201,11 @@ namespace
bool generate_type_alias(code_generator& generator, const cpp_type_alias& alias)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias));
if (output)
{
output << keyword("using") << whitespace << identifier(alias.name())
<< punctuation("=");
output << keyword("using") << whitespace << identifier(alias.name()) << operator_ws
<< punctuation("=") << operator_ws;
if (output.options() & code_generator::exclude_target)
output.excluded(alias);
else
@ -201,13 +217,13 @@ namespace
bool generate_enum_value(code_generator& generator, const cpp_enum_value& value)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(value), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(value));
if (output)
{
output << identifier(value.name());
if (value.value())
{
output << punctuation("=");
output << operator_ws << punctuation("=") << operator_ws;
detail::
write_expression(output,
value.value()
@ -219,7 +235,7 @@ namespace
bool generate_enum(code_generator& generator, const cpp_enum& e)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(e), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(e));
if (output)
{
output << keyword("enum");
@ -228,16 +244,16 @@ namespace
output << whitespace << identifier(e.semantic_scope()) << identifier(e.name());
if (e.has_explicit_type())
{
output << newl << punctuation(":");
output << newl << punctuation(":") << operator_ws;
detail::write_type(output, e.underlying_type(), "");
}
if (output.generate_definition() && e.is_definition())
{
output << punctuation("{");
output << opening_brace;
output.indent();
auto need_sep = write_container(output, e, [](code_generator::output& out) {
auto need_sep = write_container(output, e, [](const code_generator::output& out) {
out << punctuation(",") << newl;
});
if (need_sep)
@ -261,7 +277,7 @@ namespace
bool generate_access_specifier(code_generator& generator, const cpp_access_specifier& access)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(access), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(access));
if (output)
write_access_specifier(output, access.access_specifier());
return static_cast<bool>(output);
@ -273,7 +289,7 @@ namespace
detail::assert_handler{});
auto parent_kind = static_cast<const cpp_class&>(base.parent().value()).class_kind();
code_generator::output output(type_safe::ref(generator), type_safe::ref(base), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(base));
if (output)
{
if (base.is_virtual())
@ -298,7 +314,8 @@ namespace
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(">");
output << punctuation("<") << bracket_ws << token_seq(spec.unexposed_arguments())
<< bracket_ws << punctuation(">");
}
void write_bases(code_generator& generator, code_generator::output& output, const cpp_class& c)
@ -310,10 +327,10 @@ namespace
if (first && !output.options(base).is_set(code_generator::exclude))
{
first = false;
output << newl << punctuation(":");
output << newl << punctuation(":") << operator_ws;
}
else if (need_sep)
output << punctuation(",");
output << comma;
need_sep = generate_base_class(generator, base);
}
}
@ -321,7 +338,7 @@ namespace
bool generate_class(code_generator& generator, const cpp_class& c,
type_safe::optional_ref<const cpp_template_specialization> spec = nullptr)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(c), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(c));
if (output)
{
if (is_friended(c))
@ -345,7 +362,7 @@ namespace
else
{
write_bases(generator, output, c);
output << punctuation("{");
output << opening_brace;
output.indent();
auto need_sep = false;
@ -388,7 +405,7 @@ namespace
if (var.default_value())
{
output << punctuation("=");
output << operator_ws << punctuation("=") << operator_ws;
detail::write_expression(output, var.default_value().value());
}
return static_cast<bool>(output);
@ -409,7 +426,7 @@ namespace
bool generate_variable(code_generator& generator, const cpp_variable& var)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(var));
if (output)
{
write_storage_class(output, var.storage_class(), var.is_constexpr());
@ -422,7 +439,7 @@ namespace
bool generate_member_variable(code_generator& generator, const cpp_member_variable& var)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(var));
if (output)
{
if (var.is_mutable())
@ -435,13 +452,14 @@ namespace
bool generate_bitfield(code_generator& generator, const cpp_bitfield& var)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(var), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(var));
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 << operator_ws << punctuation(":") << operator_ws
<< int_literal(std::to_string(var.no_bits()));
output << punctuation(";") << newl;
}
return static_cast<bool>(output);
@ -449,7 +467,7 @@ namespace
bool generate_function_parameter(code_generator& generator, const cpp_function_parameter& param)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(param), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(param));
if (output)
write_variable_base(output, param, param.name());
return static_cast<bool>(output);
@ -457,15 +475,15 @@ namespace
void write_function_parameters(code_generator::output& output, const cpp_function_base& base)
{
output << punctuation("(");
auto need_sep = write_container(output, base.parameters(), punctuation(","));
output << punctuation("(") << bracket_ws;
auto need_sep = write_container(output, base.parameters(), comma);
if (base.is_variadic())
{
if (need_sep)
output << punctuation(",");
output << comma;
output << punctuation("...");
}
output << punctuation(")");
output << bracket_ws << punctuation(")");
}
void write_noexcept(code_generator::output& output, const cpp_function_base& base, bool need_ws)
@ -481,9 +499,9 @@ namespace
output << keyword("noexcept");
else
{
output << keyword("noexcept") << punctuation("(");
output << keyword("noexcept") << punctuation("(") << bracket_ws;
detail::write_expression(output, cond);
output << punctuation(")");
output << bracket_ws << punctuation(")");
}
}
@ -495,15 +513,17 @@ namespace
case cpp_function_declaration:
case cpp_function_definition:
if (is_pure_virtual)
output << punctuation("=") << int_literal("0");
output << operator_ws << punctuation("=") << operator_ws << int_literal("0");
output << punctuation(";") << newl;
break;
case cpp_function_defaulted:
output << punctuation("=") << keyword("default") << punctuation(";") << newl;
output << operator_ws << punctuation("=") << operator_ws << keyword("default")
<< punctuation(";") << newl;
break;
case cpp_function_deleted:
output << punctuation("=") << keyword("delete") << punctuation(";") << newl;
output << operator_ws << punctuation("=") << operator_ws << keyword("delete")
<< punctuation(";") << newl;
break;
}
}
@ -512,7 +532,7 @@ namespace
code_generator& generator, const cpp_function& func,
type_safe::optional_ref<const cpp_template_specialization> spec = nullptr)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(func));
if (output)
{
if (is_friended(func))
@ -538,12 +558,12 @@ namespace
else
output << identifier(func.name());
write_function_parameters(output, func);
write_noexcept(output, func, false);
write_noexcept(output, func, output.formatting().is_set(formatting_flags::operator_ws));
if (!(output.options() & code_generator::exclude_return)
&& detail::is_complex_type(func.return_type()))
{
output << punctuation("->");
output << operator_ws << punctuation("->") << operator_ws;
detail::write_type(output, func.return_type(), "");
}
write_function_body(output, func, false);
@ -591,11 +611,11 @@ namespace
case cpp_ref_none:
break;
case cpp_ref_lvalue:
output << punctuation("&");
output << operator_ws << punctuation("&") << operator_ws;
need_ws = false;
break;
case cpp_ref_rvalue:
output << punctuation("&&");
output << operator_ws << punctuation("&&") << operator_ws;
need_ws = false;
break;
}
@ -607,7 +627,7 @@ namespace
code_generator& generator, const cpp_member_function& func,
type_safe::optional_ref<const cpp_template_specialization> spec = nullptr)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(func));
if (output)
{
if (is_friended(func))
@ -637,12 +657,13 @@ namespace
output << identifier(func.name());
write_function_parameters(output, func);
auto need_ws = write_cv_ref(output, func);
write_noexcept(output, func, need_ws);
write_noexcept(output, func,
need_ws || output.formatting().is_set(formatting_flags::operator_ws));
if (!(output.options() & code_generator::exclude_return)
&& detail::is_complex_type(func.return_type()))
{
output << punctuation("->");
output << operator_ws << punctuation("->") << operator_ws;
detail::write_type(output, func.return_type(), "");
}
@ -654,7 +675,7 @@ namespace
bool generate_conversion_op(code_generator& generator, const cpp_conversion_op& op)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(op), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(op));
if (output)
{
if (is_friended(op))
@ -677,7 +698,8 @@ namespace
output << punctuation("(") << punctuation(")");
auto need_ws = write_cv_ref(output, op);
write_noexcept(output, op, need_ws);
write_noexcept(output, op,
need_ws || output.formatting().is_set(formatting_flags::operator_ws));
write_suffix_virtual(output, op.virtual_info());
write_function_body(output, op, is_pure(op.virtual_info()));
@ -687,7 +709,7 @@ namespace
bool generate_constructor(code_generator& generator, const cpp_constructor& ctor)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(ctor), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(ctor));
if (output)
{
if (is_friended(ctor))
@ -699,7 +721,7 @@ namespace
output << identifier(ctor.semantic_scope()) << identifier(ctor.name());
write_function_parameters(output, ctor);
write_noexcept(output, ctor, false);
write_noexcept(output, ctor, output.formatting().is_set(formatting_flags::operator_ws));
write_function_body(output, ctor, false);
}
@ -708,7 +730,7 @@ namespace
bool generate_destructor(code_generator& generator, const cpp_destructor& dtor)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(dtor), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(dtor));
if (output)
{
if (is_friended(dtor))
@ -716,7 +738,7 @@ namespace
write_prefix_virtual(output, dtor.virtual_info());
output << identifier(dtor.semantic_scope()) << identifier(dtor.name())
<< punctuation("(") << punctuation(")");
write_noexcept(output, dtor, false);
write_noexcept(output, dtor, output.formatting().is_set(formatting_flags::operator_ws));
write_suffix_virtual(output, dtor.virtual_info());
write_function_body(output, dtor, is_pure(dtor.virtual_info()));
@ -750,7 +772,7 @@ namespace
bool generate_friend(code_generator& generator, const cpp_friend& f)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(f), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(f));
if (output)
{
if (auto e = f.entity())
@ -770,17 +792,17 @@ namespace
bool 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);
code_generator::output output(type_safe::ref(generator), type_safe::ref(param));
if (output)
{
output << keyword(to_string(param.keyword()));
if (param.is_variadic())
output << punctuation("...");
output << operator_ws << punctuation("...");
if (!param.name().empty())
output << whitespace << identifier(param.name());
if (param.default_type())
{
output << punctuation("=");
output << operator_ws << punctuation("=") << operator_ws;
detail::write_type(output, param.default_type().value(), "");
}
}
@ -790,13 +812,13 @@ namespace
bool 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);
code_generator::output output(type_safe::ref(generator), type_safe::ref(param));
if (output)
{
detail::write_type(output, param.type(), param.name(), param.is_variadic());
if (param.default_value())
{
output << punctuation("=");
output << operator_ws << punctuation("=") << operator_ws;
detail::write_expression(output, param.default_value().value());
}
}
@ -806,17 +828,19 @@ namespace
bool 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);
code_generator::output output(type_safe::ref(generator), type_safe::ref(param));
if (output)
{
output << keyword("template") << punctuation("<");
output << keyword("template") << operator_ws << punctuation("<") << bracket_ws;
write_container(output, param.parameters(), punctuation(","));
output << punctuation(">") << keyword(to_string(param.keyword())) << whitespace;
output << bracket_ws << punctuation(">") << operator_ws
<< keyword(to_string(param.keyword()));
if (param.is_variadic())
output << punctuation("...");
output << identifier(param.name());
output << operator_ws << punctuation("...");
output << whitespace << identifier(param.name());
if (param.default_template())
output << punctuation("=") << param.default_template().value();
output << operator_ws << punctuation("=") << operator_ws
<< param.default_template().value();
}
return static_cast<bool>(output);
}
@ -825,7 +849,7 @@ namespace
bool hide_if_empty)
{
if (!hide_if_empty)
output << keyword("template") << punctuation("<");
output << keyword("template") << operator_ws << punctuation("<") << bracket_ws;
auto need_sep = false;
auto first = hide_if_empty;
@ -835,20 +859,20 @@ namespace
&& !output.options(*templ.parameters().begin()).is_set(code_generator::exclude))
{
first = false;
output << keyword("template") << punctuation("<");
output << keyword("template") << operator_ws << punctuation("<") << bracket_ws;
}
else if (need_sep)
output << punctuation(",");
output << comma;
need_sep = generate_code(*output.generator(), param);
}
if (!hide_if_empty || need_sep)
output << punctuation(">") << newl;
output << bracket_ws << punctuation(">") << newl;
}
bool generate_alias_template(code_generator& generator, const cpp_alias_template& alias)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(alias));
if (output)
{
write_template_parameters(output, alias, true);
@ -859,7 +883,7 @@ namespace
bool generate_variable_template(code_generator& generator, const cpp_variable_template& var)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(var), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(var));
if (output)
{
write_template_parameters(output, var, true);
@ -870,7 +894,7 @@ namespace
bool generate_function_template(code_generator& generator, const cpp_function_template& func)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(func), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(func));
if (output)
{
write_template_parameters(output, func, true);
@ -882,7 +906,7 @@ namespace
bool 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);
code_generator::output output(type_safe::ref(generator), type_safe::ref(func));
if (output)
{
DEBUG_ASSERT(func.is_full_specialization(), detail::assert_handler{});
@ -896,7 +920,7 @@ namespace
bool generate_class_template(code_generator& generator, const cpp_class_template& templ)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(templ), true);
code_generator::output output(type_safe::ref(generator), type_safe::ref(templ));
if (output)
{
write_template_parameters(output, templ, true);
@ -908,7 +932,7 @@ namespace
bool 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);
code_generator::output output(type_safe::ref(generator), type_safe::ref(templ));
if (output)
{
write_template_parameters(output, templ, false);
@ -919,20 +943,20 @@ namespace
bool generate_static_assert(code_generator& generator, const cpp_static_assert& assert)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(assert), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(assert));
if (output)
{
output << keyword("static_assert") << punctuation("(");
output << keyword("static_assert") << punctuation("(") << bracket_ws;
detail::write_expression(output, assert.expression());
output << punctuation(",") << string_literal('"' + assert.message() + '"');
output << punctuation(");") << newl;
output << comma << string_literal('"' + assert.message() + '"');
output << bracket_ws << punctuation(");") << newl;
}
return static_cast<bool>(output);
}
bool generate_unexposed(code_generator& generator, const cpp_unexposed_entity& entity)
{
code_generator::output output(type_safe::ref(generator), type_safe::ref(entity), false);
code_generator::output output(type_safe::ref(generator), type_safe::ref(entity));
if (output)
output << token_seq(entity.spelling());
return static_cast<bool>(output);
@ -1012,12 +1036,12 @@ void detail::write_template_arguments(code_generator::output&
if (arguments.size() == 0u)
return;
output << punctuation("<");
output << punctuation("<") << bracket_ws;
auto need_sep = false;
for (auto& arg : arguments)
{
if (need_sep)
output << punctuation(",");
output << comma;
else
need_sep = true;
@ -1030,5 +1054,5 @@ void detail::write_template_arguments(code_generator::output&
else
DEBUG_UNREACHABLE(detail::assert_handler{});
}
output << punctuation(">");
output << bracket_ws << punctuation(">");
}

View file

@ -190,6 +190,25 @@ bool detail::is_complex_type(const cpp_type& type) noexcept
namespace
{
void comma(const code_generator::output& output)
{
output << punctuation(",");
if (output.formatting().is_set(formatting_flags::comma_ws))
output << whitespace;
}
void bracket_ws(const code_generator::output& output)
{
if (output.formatting().is_set(formatting_flags::bracket_ws))
output << whitespace;
}
void operator_ws(const code_generator::output& output)
{
if (output.formatting().is_set(formatting_flags::operator_ws))
output << whitespace;
}
void write_builtin(code_generator::output& output, const cpp_builtin_type& type)
{
output << keyword(to_string(type.builtin_type_kind()));
@ -207,14 +226,15 @@ namespace
void write_decltype(code_generator::output& output, const cpp_decltype_type& type)
{
output << keyword("decltype") << punctuation("(");
output << keyword("decltype") << punctuation("(") << bracket_ws;
detail::write_expression(output, type.expression());
output << punctuation(")");
output << bracket_ws << punctuation(")");
}
void write_decltype_auto(code_generator::output& output, const cpp_decltype_auto_type&)
{
output << keyword("decltype") << punctuation("(") << keyword("auto") << punctuation(")");
output << keyword("decltype") << punctuation("(") << bracket_ws << keyword("auto")
<< bracket_ws << punctuation(")");
}
void write_cv_qualified_prefix(code_generator::output& output,
@ -223,7 +243,7 @@ namespace
detail::write_type_prefix(output, type.type());
if (is_direct_complex(type.type()))
output << punctuation("(");
output << punctuation("(") << bracket_ws;
if (is_const(type.cv_qualifier()))
output << whitespace << keyword("const");
@ -235,7 +255,7 @@ namespace
const cpp_cv_qualified_type& type)
{
if (is_direct_complex(type.type()))
output << punctuation(")");
output << bracket_ws << punctuation(")");
detail::write_type_suffix(output, type.type());
}
@ -248,8 +268,11 @@ namespace
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("(") << bracket_ws;
else if (output.formatting().is_set(formatting_flags::ptr_ref_var))
output << whitespace;
output << punctuation("*");
}
@ -257,15 +280,18 @@ namespace
void write_pointer_suffix(code_generator::output& output, const cpp_pointer_type& type)
{
if (pointer_requires_paren(type))
output << punctuation(")");
output << bracket_ws << 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("(");
output << punctuation("(") << bracket_ws;
else if (output.formatting().is_set(formatting_flags::ptr_ref_var))
output << whitespace;
if (type.reference_kind() == cpp_ref_lvalue)
output << punctuation("&");
@ -278,7 +304,7 @@ namespace
void write_reference_suffix(code_generator::output& output, const cpp_reference_type& type)
{
if (is_direct_complex(type.referee()))
output << punctuation(")");
output << bracket_ws << punctuation(")");
detail::write_type_suffix(output, type.referee());
}
@ -291,7 +317,11 @@ namespace
{
output << punctuation("[");
if (type.size())
{
output << bracket_ws;
detail::write_expression(output, type.size().value());
output << bracket_ws;
}
output << punctuation("]");
detail::write_type_suffix(output, type.value_type());
}
@ -304,13 +334,13 @@ namespace
template <typename T>
void write_parameters(code_generator::output& output, const T& type)
{
output << punctuation("(");
output << punctuation("(") << bracket_ws;
auto need_sep = false;
for (auto& param : type.parameter_types())
{
if (need_sep)
output << punctuation(",");
output << comma;
else
need_sep = true;
detail::write_type_prefix(output, param);
@ -319,11 +349,11 @@ namespace
if (type.is_variadic())
{
if (need_sep)
output << punctuation(",");
output << comma;
output << punctuation("...");
}
output << punctuation(")");
output << bracket_ws << punctuation(")");
}
void write_function_suffix(code_generator::output& output, const cpp_function_type& type)
@ -361,7 +391,7 @@ namespace
{
detail::write_type_prefix(output, type.return_type());
output << punctuation("(");
output << punctuation("(") << bracket_ws;
detail::write_type_prefix(output, strip_class_type(type.class_type(), nullptr, nullptr));
output << punctuation("::");
}
@ -369,7 +399,7 @@ namespace
void write_member_function_suffix(code_generator::output& output,
const cpp_member_function_type& type)
{
output << punctuation(")");
output << bracket_ws << punctuation(")");
write_parameters(output, type);
auto cv = cpp_cv_none;
@ -384,9 +414,9 @@ namespace
output << keyword("volatile");
if (ref == cpp_ref_lvalue)
output << punctuation("&");
output << operator_ws << punctuation("&") << operator_ws;
else if (ref == cpp_ref_rvalue)
output << punctuation("&&");
output << operator_ws << punctuation("&&") << operator_ws;
detail::write_type_suffix(output, type.return_type());
}
@ -395,7 +425,7 @@ namespace
const cpp_member_object_type& type)
{
detail::write_type_prefix(output, type.object_type());
output << punctuation("(");
output << punctuation("(") << bracket_ws;
DEBUG_ASSERT(!detail::is_complex_type(type.class_type()), detail::assert_handler{});
detail::write_type_prefix(output, type.class_type());
output << punctuation("::");
@ -403,7 +433,7 @@ namespace
void write_member_object_suffix(code_generator::output& output, const cpp_member_object_type&)
{
output << punctuation(")");
output << bracket_ws << punctuation(")");
}
void write_template_parameter(code_generator::output& output,
@ -419,7 +449,8 @@ namespace
if (type.arguments_exposed())
detail::write_template_arguments(output, type.arguments());
else
output << punctuation("<") << token_seq(type.unexposed_arguments()) << punctuation(">");
output << punctuation("<") << bracket_ws << token_seq(type.unexposed_arguments())
<< bracket_ws << punctuation(">");
}
void write_dependent(code_generator::output& output, const cpp_dependent_type& type)
@ -511,6 +542,6 @@ void detail::write_type(code_generator::output& output, const cpp_type& type, st
if (!name.empty())
output << whitespace << identifier(name);
if (is_variadic)
output << punctuation("...");
output << operator_ws << punctuation("...") << operator_ws;
write_type_suffix(output, type);
}

View file

@ -10,11 +10,14 @@ using namespace cppast;
TEST_CASE("code_generator")
{
SECTION("basic")
{
// 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;
// no need to check much here, as each entity check separately
auto code = R"(using type=int;
type* var;
template<template<typename>class... T>
struct templated{
};
struct foo{
int a;
@ -36,10 +39,62 @@ enum class bar
void func(int(*)(int));
extern void(* ptr)(int(*)(int))=&func;)";
auto file = parse({}, "code_generator.cpp", code);
auto file = parse({}, "code_generator.cpp", code);
SECTION("basic")
{
REQUIRE(get_code(*file) == code);
}
SECTION("formatting")
{
auto synopsis = R"(using type = int;
type* var;
template <template <typename> class ... T>
struct templated
{
};
struct foo
{
int a;
auto func(int) -> int(*(*)(int))[42];
private:
int const b = 42;
};
int(*(*(foo::* mptr)(int))(int))[42];
enum class bar
: int
{
a,
b = 42
};
void func(int(*)(int));
extern void(* ptr)(int(*)(int)) = &func;
)";
class formatted_generator : public test_generator
{
public:
using test_generator::test_generator;
private:
formatting do_get_formatting() const override
{
return formatting_flags::brace_nl | formatting_flags::operator_ws
| formatting_flags::comma_ws;
}
} generator(code_generator::generation_options{});
generate_code(generator, *file);
REQUIRE(generator.str() == synopsis);
}
SECTION("exclude target")
{
auto code = R"(