Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide an option to change static method name when it conflicts with instance name #308

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -119,13 +119,13 @@ add_library(libchimera
)
target_compile_options(libchimera PUBLIC "-std=c++11")
target_link_libraries(libchimera
PRIVATE
PUBLIC
${YAMLCPP_LIBRARIES}
${CLANG_LIBS}
${llvm_libs}
)
target_link_libraries(libchimera PRIVATE mstch cling_utils)
target_link_libraries(libchimera PRIVATE chimera_bindings)
target_link_libraries(libchimera PUBLIC mstch cling_utils)
target_link_libraries(libchimera PUBLIC chimera_bindings)
target_compile_definitions(libchimera
PUBLIC
CHIMERA_MAJOR_VERSION=${CHIMERA_MAJOR_VERSION}
Expand Down
25 changes: 24 additions & 1 deletion include/chimera/configuration.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,15 +120,37 @@ class Configuration
class CompiledConfiguration
{
public:
enum class StaticMethodNamePolicy
{
NO_CHANGE,
// Cast all the letters to upper case
TO_UPPER,
// Cast all the letters to lower case
TO_LOWER,
// Cast the first letter to upper case and cast all the letters followed
// by underscore to upper cases removing the underscores
TO_PASCAL,
// Cast the first letter to lower case and cast all the letters followed
// by underscore to upper cases removing the underscores
TO_CAMEL,
// TODO: Add more policies such as prefix and suffix
};

virtual ~CompiledConfiguration() = default;
CompiledConfiguration(const CompiledConfiguration &) = delete;
CompiledConfiguration &operator=(const CompiledConfiguration &) = delete;

/**
* Return whether to treat unresolvable configuration as errors.
* Returns whether to treat unresolvable configuration as errors.
*/
bool GetStrict() const;

/**
* Returns policy for the case that a static method has a same name one of
* the instance method names in the same class.
*/
StaticMethodNamePolicy GetStaticMethodNamePolicy() const;

/**
* Adds a namespace to an ordered set of traversed namespaces.
* This set can later be rendered in a template.
Expand Down Expand Up @@ -258,6 +280,7 @@ class CompiledConfiguration
std::set<const clang::NamespaceDecl *> binding_namespace_decls_;

bool strict_;
StaticMethodNamePolicy static_method_name_policy_;

friend class Configuration;
};
Expand Down
16 changes: 13 additions & 3 deletions include/chimera/mstch.h
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ class Function : public ClangWrapper<clang::FunctionDecl>,
const int argument_limit = -1);

::mstch::node type();
::mstch::node overloads();
virtual ::mstch::node overloads();
::mstch::node params();
::mstch::node returnType();
::mstch::node returnValuePolicy();
Expand All @@ -295,8 +295,10 @@ class Function : public ClangWrapper<clang::FunctionDecl>,
::mstch::node call();
::mstch::node qualifiedCall();

private:
protected:
const clang::CXXRecordDecl *class_decl_;

private:
const int argument_limit_;
};

Expand All @@ -305,15 +307,23 @@ class Method : public Function
public:
Method(const ::chimera::CompiledConfiguration &config,
const clang::CXXMethodDecl *decl,
const clang::CXXRecordDecl *class_decl = nullptr);
const clang::CXXRecordDecl *class_decl = nullptr,
const int argument_limit = -1);

void setNameConflict(bool val);
bool isNameConflict() const;

::mstch::node overloads() override;
std::string nameAsString() override;
::mstch::node isConst();
bool isStaticAsBool() const;
::mstch::node isStatic();
::mstch::node isVirtual();
::mstch::node isPureVirtual();

private:
const clang::CXXMethodDecl *method_decl_;
bool name_conflict_;
};

class Namespace : public ClangWrapper<clang::NamespaceDecl>
Expand Down
32 changes: 32 additions & 0 deletions include/chimera/util.h
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,38 @@ std::string trimLeft(std::string s, const char *t = " \t\n\r\f\v");
*/
std::string trim(std::string s, const char *t = " \t\n\r\f\v");

/**
* Casts all the letters to upper case
*/
std::string toUpper(const std::string &str, int n = -1);

/**
* Casts all the letters to lower case
*/
std::string toLower(const std::string &str, int n = -1);

/**
* Casts the first letter to upper case and casts all the letters followed by
* underscore to upper cases removing the underscores
*/
std::string toPascal(const std::string &str);

/**
* Casts the first letter to lower case and casts all the letters followed by
* underscore to upper cases removing the underscores
*/
std::string toCamel(const std::string &str);

/**
* Splits string into a list of tokens.
*
* @param[in] str The original string to be split.
* @param[in] delimiter The string delimiter, which can be more than one
* character.
*/
std::vector<std::string> split(const std::string &str,
const std::string &delimiter = " ");

/**
* Returns true if a string starts with a prefix, otherwise false.
*/
Expand Down
59 changes: 55 additions & 4 deletions src/configuration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,51 @@ chimera::CompiledConfiguration::CompiledConfiguration(
config_options_strict = strictNode.as<bool>();
}

// Parse 'options.static_method_name_policy' to set the static method name
// policy for the case a static method has the same name one of the instance
// methods in the same class.
static_method_name_policy_ = StaticMethodNamePolicy::NO_CHANGE;
const YAML::Node staticMethodNamePolicyNode = chimera::util::lookupYAMLNode(
configNode_, "options", "static_method_name_policy");
if (staticMethodNamePolicyNode)
{
const std::string policy = staticMethodNamePolicyNode.as<std::string>();
if (policy == "to_upper")
{
static_method_name_policy_ = StaticMethodNamePolicy::TO_UPPER;
}
else if (policy == "to_lower")
{
static_method_name_policy_ = StaticMethodNamePolicy::TO_UPPER;
}
else if (policy == "to_pascal")
{
static_method_name_policy_ = StaticMethodNamePolicy::TO_PASCAL;
}
else if (policy == "to_camel")
{
static_method_name_policy_ = StaticMethodNamePolicy::TO_CAMEL;
}
else
{
if (GetStrict())
{
throw std::runtime_error(
"Invalid value for 'static_method_name_policy': '" + policy
+ "'. Choose one of following: 'to_upper', 'to_lower', 'to_camel', 'to_pascal'");
}
else
{
std::cerr
<< "Invalid value for 'static_method_name_policy': '"
<< policy
<< "'. Choose one of following: 'no_change', 'to_upper', "
<< "'to_lower', 'to_camel', 'to_pascal'. Defaulting to "
<< "'no_change'." << std::endl;
}
}
}

// Set the options.strict from one of the following sources in order of
// priority: 1) CLI '-strict' setting (True if -strict passed, False
// otherwise) 2) YAML configuration setting (True/False) 3) false by default
Expand Down Expand Up @@ -268,7 +313,7 @@ chimera::CompiledConfiguration::CompiledConfiguration(
}
else
{
std::cout << "Warning: Skipped namespace namespace '"
std::cerr << "Warning: Skipped namespace namespace '"
<< ns_str
<< "' because it's unable to resolve "
<< "the namespace." << std::endl;
Expand Down Expand Up @@ -324,7 +369,7 @@ chimera::CompiledConfiguration::CompiledConfiguration(
}
else
{
std::cout
std::cerr
<< "Warning: Skipped the configuration for class '"
<< decl_str << "' becuase it's "
<< "unable to resolve the class declaration."
Expand Down Expand Up @@ -363,7 +408,7 @@ chimera::CompiledConfiguration::CompiledConfiguration(
}
else
{
std::cout
std::cerr
<< "Warning: Skipped the configuration for "
<< "function '" << decl_str << "' becuase it's "
<< "unable to resolve the function declaration."
Expand Down Expand Up @@ -402,7 +447,7 @@ chimera::CompiledConfiguration::CompiledConfiguration(
}
else
{
std::cout << "Warning: Skipped the configuration for "
std::cerr << "Warning: Skipped the configuration for "
<< "type '" << type_str << "' becuase it's "
<< "unable to resolve the type." << std::endl;
continue;
Expand Down Expand Up @@ -478,6 +523,12 @@ bool chimera::CompiledConfiguration::GetStrict() const
return strict_;
}

chimera::CompiledConfiguration::StaticMethodNamePolicy
chimera::CompiledConfiguration::GetStaticMethodNamePolicy() const
{
return static_method_name_policy_;
}

void chimera::CompiledConfiguration::AddTraversedNamespace(
const clang::NamespaceDecl *decl)
{
Expand Down
110 changes: 105 additions & 5 deletions src/mstch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
#include "chimera/util.h"
#include "cling_utils_AST.h"

#include <algorithm>
#include <iostream>
#include <sstream>
#include <unordered_set>
#include <vector>
#include <boost/variant/get.hpp>

Expand Down Expand Up @@ -479,8 +481,31 @@ ::mstch::node CXXRecord::methods()
method_vector.back()->setLast(true);

// Copy each template into the mstch template array.
for (auto method_template : method_vector)
method_templates.push_back(method_template);
for (const std::shared_ptr<Method> &method : method_vector)
method_templates.push_back(method);

// Collect all the non-static method names
std::unordered_set<std::string> non_static_method_names;
for (const std::shared_ptr<Method> &method : method_vector)
{
if (!method->isStaticAsBool())
non_static_method_names.insert(method->nameAsString());
}

// Set each static member whether it has the same name of the instance
// methods in the same class, which is not allowed by Python. How to handle
// the name conflict is determined by the configuration (see also
// CompiledConfiguration::StaticMethodNamePolicy).
for (const std::shared_ptr<Method> &method : method_vector)
{
if (method->isStaticAsBool())
{
auto it = non_static_method_names.find(method->nameAsString());
if (it != non_static_method_names.end())
method->setNameConflict(true);
}
}

return method_templates;
}

Expand Down Expand Up @@ -968,8 +993,11 @@ ::mstch::node Function::isTemplate()
}

Method::Method(const ::chimera::CompiledConfiguration &config,
const CXXMethodDecl *decl, const CXXRecordDecl *class_decl)
: Function(config, decl, class_decl), method_decl_(decl)
const CXXMethodDecl *decl, const CXXRecordDecl *class_decl,
const int argument_limit)
: Function(config, decl, class_decl, argument_limit)
, method_decl_(decl)
, name_conflict_(false)
{
register_methods(this, {
{"is_const", &Method::isConst},
Expand All @@ -979,16 +1007,88 @@ Method::Method(const ::chimera::CompiledConfiguration &config,
});
}

void Method::setNameConflict(bool val)
{
name_conflict_ = val;
}

bool Method::isNameConflict() const
{
return name_conflict_;
}

::mstch::node Method::overloads()
{
::mstch::array overloads;

// Create a list of wrappers that re-wrap this function with a subset of
// the full set of arguments (i.e. omitting some of the default arguments).
auto arg_range = chimera::util::getFunctionArgumentRange(decl_);
for (unsigned n_args = arg_range.first; n_args < arg_range.second; ++n_args)
{
auto method = std::make_shared<Method>(config_, method_decl_,
class_decl_, n_args);
method->setNameConflict(name_conflict_);
overloads.push_back(method);
}

// Add this function to its own list of overloads.
// It can be distinguished from other copies because `uses_defaults=false`.
overloads.push_back(shared_from_this());

return overloads;
}

std::string Method::nameAsString()
{
std::string original_name = Function::nameAsString();

// TODO: This shouldn't happen
if (original_name.empty())
return original_name;

if (isStaticAsBool() && name_conflict_)
{
switch (config_.GetStaticMethodNamePolicy())
{
case CompiledConfiguration::StaticMethodNamePolicy::NO_CHANGE:
// Do nothing
break;
case CompiledConfiguration::StaticMethodNamePolicy::TO_UPPER:
original_name = chimera::util::toUpper(original_name);
break;
case CompiledConfiguration::StaticMethodNamePolicy::TO_LOWER:
original_name = chimera::util::toLower(original_name);
break;
case CompiledConfiguration::StaticMethodNamePolicy::TO_PASCAL:
original_name = chimera::util::toPascal(original_name);
break;
case CompiledConfiguration::StaticMethodNamePolicy::TO_CAMEL:
original_name = chimera::util::toCamel(original_name);
break;
default:
break;
}
}

return original_name;
}

::mstch::node Method::isConst()
{
return method_decl_->isConst();
}

::mstch::node Method::isStatic()
bool Method::isStaticAsBool() const
{
return method_decl_->isStatic();
}

::mstch::node Method::isStatic()
{
return isStaticAsBool();
}

::mstch::node Method::isVirtual()
{
return method_decl_->isVirtual();
Expand Down
Loading