382 lines
12 KiB
C++
382 lines
12 KiB
C++
/**
|
|
* @file
|
|
*
|
|
* @copyright
|
|
* @verbatim
|
|
Copyright @ 2017 Audi Electronics Venture GmbH. All rights reserved.
|
|
|
|
This Source Code Form is subject to the terms of the Mozilla
|
|
Public License, v. 2.0. If a copy of the MPL was not distributed
|
|
with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
|
|
If it is not possible or desirable to put the notice in a particular file, then
|
|
You may include the notice in a location (such as a LICENSE file in a
|
|
relevant directory) where a recipient would be likely to look for such a notice.
|
|
|
|
You may add additional accurate notices of copyright ownership.
|
|
@endverbatim
|
|
*/
|
|
|
|
#include <ddl.h>
|
|
#include "header_printer.h"
|
|
#include "header_header.h"
|
|
#include "header_basic_type.h"
|
|
|
|
namespace ddl_generator
|
|
{
|
|
namespace oo
|
|
{
|
|
//define all needed error types and values locally
|
|
_MAKE_RESULT(0, ERR_NOERROR)
|
|
_MAKE_RESULT(-4, ERR_POINTER)
|
|
_MAKE_RESULT(-5, ERR_INVALID_ARG)
|
|
_MAKE_RESULT(-11, ERR_INVALID_FILE)
|
|
_MAKE_RESULT(-20, ERR_NOT_FOUND)
|
|
_MAKE_RESULT(-24, ERR_PATH_NOT_FOUND)
|
|
_MAKE_RESULT(-27, ERR_OPEN_FAILED)
|
|
_MAKE_RESULT(-38, ERR_FAILED)
|
|
}
|
|
}
|
|
|
|
using namespace ddl_generator::oo;
|
|
|
|
using namespace ddl;
|
|
|
|
namespace ddl
|
|
{
|
|
|
|
HeaderPrinter::HeaderPrinter() : _header(NULL)
|
|
{ }
|
|
|
|
HeaderPrinter::~HeaderPrinter()
|
|
{ }
|
|
|
|
a_util::result::Result HeaderPrinter::writeToFile(const a_util::filesystem::Path &filename)
|
|
{
|
|
std::string guarded_header_output = addHeaderGuards(filename, _header_output);
|
|
if (a_util::filesystem::writeTextFile(filename, guarded_header_output) != a_util::filesystem::OK)
|
|
{
|
|
return (ERR_OPEN_FAILED);
|
|
}
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
const std::string& HeaderPrinter::getHeader()
|
|
{
|
|
return _header_output;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::visit(const Header* header)
|
|
{
|
|
_header = header;
|
|
_header_output = "";
|
|
_header_output.append("// This is a generated file, changes to it may be overwritten in the future.\n\n");
|
|
|
|
if (_name_space.length() > 0)
|
|
{
|
|
_header_output.append(a_util::strings::format("namespace %s\n{\n", _name_space.c_str()));
|
|
}
|
|
|
|
HeaderTypedefs typedefs = header->getTypedefs();
|
|
for (HeaderTypedefs::iterator iter = typedefs.begin(); iter != typedefs.end(); iter++)
|
|
{
|
|
if (isFailed((appendType(*iter))))
|
|
{
|
|
return ERR_FAILED;
|
|
}
|
|
}
|
|
|
|
HeaderEnums enums = header->getEnums();
|
|
for (auto iter = enums.begin(); iter != enums.end(); iter++)
|
|
{
|
|
if (isFailed(((*iter)->accept(this))))
|
|
{
|
|
return ERR_FAILED;
|
|
}
|
|
}
|
|
|
|
HeaderStructs structs = header->getStructs();
|
|
for (HeaderStructs::iterator iter = structs.begin(); iter != structs.end(); iter++)
|
|
{
|
|
if (isFailed((appendType(*iter))))
|
|
{
|
|
return ERR_FAILED;
|
|
}
|
|
}
|
|
HeaderConstants constants = header->getConstants();
|
|
for (HeaderConstants::iterator iter = constants.begin(); iter != constants.end(); iter++)
|
|
{
|
|
if (isFailed(((*iter)->accept(this))))
|
|
{
|
|
return ERR_FAILED;
|
|
}
|
|
}
|
|
|
|
if (isFailed((printUnknownTypes())))
|
|
{
|
|
return ERR_FAILED;
|
|
}
|
|
|
|
if (_name_space.length() > 0)
|
|
{
|
|
_header_output.append(a_util::strings::format("} // namespace %s\n", _name_space.c_str()));
|
|
}
|
|
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::visit(const HeaderBasicType* basic_type)
|
|
{
|
|
CollectType(basic_type, true);
|
|
_header_output.append("// The following type is assumed to be known:\n");
|
|
_header_output.append("// ");
|
|
_header_output.append(basic_type->getName());
|
|
_header_output.append("\n\n");
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::visit(const HeaderTypedef* type_def)
|
|
{
|
|
printDescription(type_def);
|
|
// Set known first, so no infinite loop will occur when looking for base type
|
|
CollectType(type_def, true);
|
|
appendType(type_def->getOriginalType());
|
|
_header_output.append(a_util::strings::format("typedef %s %s;\n\n",
|
|
type_def->getOriginalType()->getName().c_str(), type_def->getName().c_str()));
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::visit(const HeaderConstant* constant)
|
|
{
|
|
appendType(constant->getType());
|
|
_header_output.append("const ");
|
|
_header_output.append(constant->getType()->getName());
|
|
_header_output.append(" ");
|
|
_header_output.append(constant->getName());
|
|
_header_output.append(" = ");
|
|
_header_output.append(constant->asString());
|
|
_header_output.append(";\n\n");
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::visit(const HeaderStruct* header_struct)
|
|
{
|
|
printDescription(header_struct);
|
|
// Set known first, so no infinite loop will occur when looking for base type
|
|
CollectType(header_struct, true);
|
|
// Make sure all types are already defined
|
|
HeaderStructElementVec elements = header_struct->getElements();
|
|
for (HeaderStructElementVec::iterator iter = elements.begin(); iter != elements.end(); iter++)
|
|
{
|
|
appendType((*iter)->getType());
|
|
}
|
|
_header_output.append(a_util::strings::format("#pragma pack(push,%d)\n", header_struct->getPacking()));
|
|
_header_output.append("typedef struct\n{\n");
|
|
for (HeaderStructElementVec::iterator iter = elements.begin(); iter != elements.end(); iter++)
|
|
{
|
|
if (isFailed(((*iter)->accept(this))))
|
|
{
|
|
return ERR_FAILED;
|
|
}
|
|
}
|
|
_header_output.append(a_util::strings::format("} %s;\n", header_struct->getName().c_str()));
|
|
_header_output.append("#pragma pack(pop)\n\n");
|
|
CollectType(header_struct, true);
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::visit(const HeaderStructElement* header_struct)
|
|
{
|
|
if (!header_struct || !header_struct->getType())
|
|
{
|
|
return ERR_INVALID_ARG;
|
|
}
|
|
printDescription(header_struct->getDescription(), header_struct->getComment(), true);
|
|
appendType(header_struct->getType());
|
|
_header_output.append(" ");
|
|
if (header_struct->isStatic())
|
|
{
|
|
_header_output.append("static ");
|
|
}
|
|
if (header_struct->isConst())
|
|
{
|
|
_header_output.append("const ");
|
|
}
|
|
_header_output.append(header_struct->getType()->getName());
|
|
if (header_struct->isPointer())
|
|
{
|
|
_header_output.append("* ");
|
|
}
|
|
_header_output.append(" ");
|
|
_header_output.append(header_struct->getName());
|
|
if (header_struct->getArraySize() > 1)
|
|
{
|
|
_header_output.append(a_util::strings::format("[%d]", header_struct->getArraySize()));
|
|
}
|
|
_header_output.append(";\n");
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::visit(const HeaderEnum* header_enum)
|
|
{
|
|
CollectType(header_enum, true);
|
|
printDescription(header_enum);
|
|
_header_output.append("typedef enum {\n");
|
|
for (auto iter = header_enum->getValues().begin(); iter != header_enum->getValues().end(); iter++)
|
|
{
|
|
_header_output.append(a_util::strings::format(" %s=%i,\n", iter->second.c_str(), iter->first));
|
|
}
|
|
if (header_enum->getValues().size() > 0)
|
|
{
|
|
// remove last ',' since some versions of C/C++ don't allow trailing commas in enums
|
|
_header_output.resize(_header_output.length() - 2);
|
|
}
|
|
_header_output.append(a_util::strings::format("\n} %s;\n\n", header_enum->getName().c_str()));
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::appendType(const HeaderType* type)
|
|
{
|
|
// See if we already know this type and therefore have printed it already
|
|
for (HeaderConstTypes::iterator iter = _known_types.begin(); iter != _known_types.end(); iter++)
|
|
{
|
|
if (*iter == type)
|
|
{
|
|
return ERR_NOERROR;
|
|
}
|
|
}
|
|
// Search the header for the type.
|
|
if (_header != NULL)
|
|
{
|
|
for (HeaderTypedefs::const_iterator iter = _header->getTypedefs().begin(); iter != _header->getTypedefs().end(); iter++)
|
|
{
|
|
if (*iter == type)
|
|
{
|
|
// Found it, append it
|
|
return ((*iter)->accept(this));
|
|
}
|
|
}
|
|
for (HeaderStructs::const_iterator iter = _header->getStructs().begin(); iter != _header->getStructs().end(); iter++)
|
|
{
|
|
if (*iter == type)
|
|
{
|
|
// Found it, append it
|
|
return ((*iter)->accept(this));
|
|
}
|
|
}
|
|
}
|
|
// Nothing found so far, so type is unknown
|
|
CollectType(type, false);
|
|
return (ERR_NOT_FOUND);
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::CollectType(const ddl::HeaderType* type, bool is_known)
|
|
{
|
|
if (is_known)
|
|
{
|
|
if (std::find(_known_types.begin(), _known_types.end(), type) ==
|
|
_known_types.end())
|
|
{
|
|
_known_types.push_back(type);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (std::find(_unknown_types.begin(), _unknown_types.end(), type) ==
|
|
_unknown_types.end())
|
|
{
|
|
_unknown_types.push_back(type);
|
|
}
|
|
}
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::printUnknownTypes()
|
|
{
|
|
// visit all types that are in the unknown types set but not in the knowN types set
|
|
HeaderConstTypes vec = _unknown_types;
|
|
for (HeaderConstTypes::iterator iter = vec.begin(); iter != vec.end(); iter++)
|
|
{
|
|
if (std::find(_known_types.begin(), _known_types.end(), *iter) == _known_types.end())
|
|
{
|
|
(*iter)->accept(this);
|
|
}
|
|
}
|
|
if (vec.size() != _unknown_types.size())
|
|
{
|
|
printUnknownTypes();
|
|
}
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::printDescription(const std::string& description, const std::string& comment, bool indent)
|
|
{
|
|
if (description.length() == 0 && comment.length() == 0)
|
|
{
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
std::string indent_string;
|
|
if (indent)
|
|
{
|
|
indent_string = " ";
|
|
}
|
|
|
|
_header_output.append(indent_string);
|
|
_header_output.append("/**\n");
|
|
if (description.length() > 0)
|
|
{
|
|
_header_output.append(a_util::strings::format("%s * %s\n", indent_string.c_str(), description.c_str()));
|
|
}
|
|
if (comment.length() > 0)
|
|
{
|
|
_header_output.append(a_util::strings::format("%s * %s\n", indent_string.c_str(), comment.c_str()));
|
|
}
|
|
_header_output.append(indent_string);
|
|
_header_output.append("*/\n");
|
|
return ERR_NOERROR;
|
|
}
|
|
|
|
a_util::result::Result HeaderPrinter::printDescription(const HeaderType* type)
|
|
{
|
|
return printDescription(type->getDescription(), type->getComment());
|
|
}
|
|
|
|
bool invalidHeaderChar(char c)
|
|
{
|
|
bool is_num = (c >= '0' && c <= '9');
|
|
bool is_uppercase_alpha = (c >= 'A' && c <= 'Z');
|
|
bool is_lowercase_alpha = (c >= 'a' && c <= 'z');
|
|
bool is_allowed_punctuation = (c == '.' || c == '_' || c == '-');
|
|
return !(is_num || is_uppercase_alpha || is_lowercase_alpha || is_allowed_punctuation);
|
|
}
|
|
|
|
std::string HeaderPrinter::addHeaderGuards(const a_util::filesystem::Path &filename, const std::string &unguarded_header_content)
|
|
{
|
|
std::string guard_name = filename.getLastElement().toString();
|
|
guard_name.erase(std::remove_if(guard_name.begin(), guard_name.end(), invalidHeaderChar), guard_name.end());
|
|
std::replace(guard_name.begin(), guard_name.end(), '.', '_');
|
|
std::transform(guard_name.begin(), guard_name.end(), guard_name.begin(), ::toupper);
|
|
|
|
std::string output;
|
|
|
|
output.append("#ifndef ");
|
|
output.append(guard_name + "\n");
|
|
output.append("#define ");
|
|
output.append(guard_name + "\n\n");
|
|
|
|
output.append(unguarded_header_content);
|
|
|
|
output.append("\n#endif //");
|
|
output.append(guard_name + "\n");
|
|
|
|
return output;
|
|
}
|
|
|
|
void HeaderPrinter::SetNamespace(std::string name_space)
|
|
{
|
|
_name_space = name_space;
|
|
}
|
|
|
|
} // namespace ddl
|