* Copyright (c) 2021 Huawei Device Co., Ltd.
*
* HDF is dual licensed: you can use it either under the terms of
* the GPL, or the BSD license, at your option.
* See the LICENSE file in the root of this repository for complete details.
*/
#include <set>
#include "file.h"
#include "logger.h"
#include "opcode.h"
#include "text_gen.h"
using namespace OHOS::Hardware;
constexpr static const char *FILE_HEAD_COMMENT =
"/*\n"
" * This is an automatically generated HDF config file. Do not modify it manually.\n"
" */\n\n";
TextGen::TextGen(std::shared_ptr<Ast> ast) : Generator(ast) {}
bool TextGen::Output()
{
if (!Initialize() || !DuplicateNodeNameCheck()) {
return false;
}
auto ret = HeaderOutput();
if (ret) {
ret = ImplOutput();
}
if (!ret && (ofs_.is_open() && !ofs_.good())) {
Logger().Error() << "failed to write file:" << outputFileName_;
}
return ret;
}
bool TextGen::Initialize()
{
auto opt = Option::Instance();
prefix_ = opt.GetSymbolPrefix();
if (prefix_.empty()) {
prefix_ = "HdfConfig";
}
prefix_ = ToLowerCamelString(prefix_);
auto moduleTerm = ast_->GetAstRoot()->Lookup("module", PARSEROP_CONFTERM);
if (moduleTerm == nullptr) {
return false;
}
moduleName_ = ToUpperCamelString(moduleTerm->Child()->StringValue());
rootVariableName_ = "g_" + prefix_ + moduleName_ + "ModuleRoot";
return true;
}
bool TextGen::HeaderOutput()
{
if (!InitOutput(".h")) {
return false;
}
ofs_ << FILE_HEAD_COMMENT;
std::string headerMacro = GetHeaderProtectMacro(outputFileName_);
ofs_ << "#ifndef " << headerMacro << "\n";
ofs_ << "#define " << headerMacro << "\n\n";
ofs_ << "#include <stdint.h>\n\n";
if (!HeaderOutputTraversal()) {
return false;
}
ofs_ << "\n#endif // " << headerMacro << '\n';
return ofs_.good();
}
bool TextGen::HeaderOutputTraversal()
{
auto ret = ast_->WalkBackward([this](const std::shared_ptr<AstObject> ¤t, int32_t) -> uint32_t {
if (!current->IsNode()) {
return NOERR;
}
auto node = ConfigNode::CastFrom(current);
if (node->GetNodeType() == NODE_INHERIT) {
return NOERR;
}
return GenNodeDefinition(current);
});
if (!ret) {
return false;
}
ofs_ << "const struct " << ToUpperCamelString(prefix_) << moduleName_ << "Root* HdfGet" << moduleName_
<< "ModuleConfigRoot(void);\n";
return ofs_.good();
}
bool TextGen::ImplOutput()
{
if (!InitOutput(".c")) {
return false;
}
symMap.clear();
ofs_ << FILE_HEAD_COMMENT;
ofs_ << "#include \"" << outputNameBase_ << ".h\"\n\n";
bool ret = OutputTemplateImpl();
if (ret) {
ret = OutputImplGlobalVariables();
}
if (!ret) {
return ret;
}
ofs_ << "\nconst struct " << ToUpperCamelString(prefix_) << moduleName_ << "Root* HdfGet" << moduleName_
<< "ModuleConfigRoot(void)\n"
<< "{\n"
<< Indent() << "return &" << rootVariableName_ << ";\n"
<< "}\n";
return ofs_.good();
}
std::string TextGen::ToUpperCamelString(const std::string &str)
{
if (str.empty()) {
return str;
}
auto out = ToCamelString(str);
out[0] = static_cast<char>(toupper(out[0]));
return out;
}
std::string TextGen::ToLowerCamelString(const std::string &str)
{
if (str.empty()) {
return str;
}
auto out = ToCamelString(str);
out[0] = static_cast<char>(tolower(out[0]));
return out;
}
std::string TextGen::ToCamelString(const std::string &str)
{
if (str.empty()) {
return str;
}
if (str.find('_') == std::string::npos) {
return str;
}
std::string out;
char cb = '\0';
constexpr char underLine = '_';
for (auto c : str) {
if (c == '_') {
cb = c;
continue;
}
if (cb == underLine) {
out.push_back(static_cast<char>(toupper(c)));
} else {
out.push_back(c);
}
cb = c;
}
return out;
}
bool TextGen::InitOutput(const std::string &fileSuffix)
{
ofs_.close();
outputFileName_ = Option::Instance().GetOutputName();
if (outputFileName_.empty()) {
outputFileName_ = Option::Instance().GetSourceNameBase();
}
outputFileName_ = Util::File::StripSuffix(outputFileName_).append(fileSuffix);
outputNameBase_ = Util::File::FileNameBase(outputFileName_);
ofs_.open(outputFileName_, std::ostream::out | std::ostream::binary);
if (!ofs_.is_open()) {
Logger().Error() << "failed to open output file: " << outputFileName_;
return false;
}
return true;
}
const std::string &TextGen::ToUpperString(std::string &str)
{
for (char &i : str) {
i = static_cast<char>(toupper(i));
}
return str;
}
std::string TextGen::GetHeaderProtectMacro(const std::string &headerFileName)
{
return std::string().append("HCS_CONFIG_").append(ToUpperString(outputNameBase_)).append("_HEADER_H");
}
uint32_t TextGen::GenNodeDefinition(const std::shared_ptr<AstObject> &node)
{
auto structName = GenConfigStructName(node);
static std::set<std::string> symbolSet;
if (symbolSet.find(structName) != symbolSet.end()) {
return NOERR;
} else {
symbolSet.insert(structName);
}
ofs_ << "struct " << structName << " {\n";
auto termIt = node->Child();
while (termIt != nullptr) {
bool res = GenObjectDefinitionGen(termIt);
if (!res) {
return res;
}
termIt = termIt->Next();
}
ofs_ << "};\n\n";
return ofs_.good() ? NOERR : EOUTPUT;
}
std::string TextGen::GenConfigStructName(const std::shared_ptr<AstObject> &node)
{
return ToUpperCamelString(prefix_).append(ToUpperCamelString(moduleName_)).append(ToUpperCamelString(node->Name()));
}
bool TextGen::GenObjectDefinitionGen(const std::shared_ptr<AstObject> &object)
{
if (!object->IsNode() && !object->IsTerm()) {
return true;
}
switch (object->Type()) {
case PARSEROP_CONFNODE: {
auto structName = GenConfigStructName(object);
auto node = ConfigNode::CastFrom(object);
auto nodeName = ToLowerCamelString(node->Name());
if (node->GetNodeType() == NODE_TEMPLATE) {
ofs_ << Indent() << "const struct " << structName << "* " << nodeName << ";\n";
ofs_ << Indent() << "uint16_t " << nodeName << "Size;\n";
} else if (node->GetNodeType() == NODE_INHERIT) {
return true;
} else {
ofs_ << Indent() << "struct " << structName << " " << nodeName << ";\n";
}
break;
}
case PARSEROP_CONFTERM:
return GenTermDefinition(object);
default:
break;
}
return ofs_.good();
}
bool TextGen::GenTermDefinition(const std::shared_ptr<AstObject> &term)
{
auto value = term->Child();
switch (value->Type()) {
case PARSEROP_ARRAY: {
auto array = ConfigArray::CastFrom(value);
if (IsInTemplate(term)) {
ofs_ << TAB << "const " << TypeToStr(array->ArrayType()) << "* " << term->Name() << ";\n";
ofs_ << TAB << "uint32_t " << term->Name() << "Size;\n";
} else {
ofs_ << TAB << TypeToStr(array->ArrayType()) << " " << term->Name() << "[" << std::dec
<< array->ArraySize() << "];\n";
}
break;
}
case PARSEROP_UINT8:
case PARSEROP_UINT16:
case PARSEROP_UINT32:
case PARSEROP_UINT64:
case PARSEROP_STRING:
ofs_ << TAB << TypeToStr(value->Type()) << " " << term->Name() << ";\n";
break;
case PARSEROP_NODEREF: {
auto structName = GenConfigStructName(ConfigTerm::CastFrom(term)->RefNode().lock());
ofs_ << TAB << "const struct " << structName << "* " << term->Name() << ";\n";
break;
}
default:
break;
}
return ofs_.good();
}
bool TextGen::IsInTemplate(const std::shared_ptr<AstObject> &object)
{
auto p = object->Parent();
while (p != nullptr) {
if (p->IsNode() && ConfigNode::CastFrom(p)->GetNodeType() == NODE_TEMPLATE) {
return true;
}
p = p->Parent();
}
return false;
}
const std::string &TextGen::TypeToStr(uint32_t type)
{
static std::map<uint32_t, std::string> typeMap = {
{PARSEROP_UINT8, "uint8_t" },
{PARSEROP_UINT16, "uint16_t" },
{PARSEROP_UINT32, "uint32_t" },
{PARSEROP_UINT64, "uint64_t" },
{PARSEROP_STRING, "const char*"},
};
return typeMap[type];
}
bool TextGen::OutputImplGlobalVariables()
{
auto forwardWalkFunc = [this](const std::shared_ptr<AstObject> ¤t, int32_t depth) -> uint32_t {
return ImplementGenTraversal(current, depth);
};
auto backwardWalkFunc = [this](const std::shared_ptr<AstObject> ¤t, int32_t depth) -> uint32_t {
return ImplementCloseBraceGen(current, depth);
};
return ast_->WalkRound(forwardWalkFunc, backwardWalkFunc);
}
const std::string &TextGen::Indent(uint32_t times)
{
static std::map<uint32_t, std::string> indentMap;
auto indent = indentMap.find(times);
if (indent == indentMap.end()) {
auto str = std::string();
for (uint32_t i = 0; i < times; ++i) {
str.append(TAB);
}
indentMap.emplace(std::pair<uint32_t, std::string>(times, std::move(str)));
}
return indentMap.at(times);
}
uint32_t TextGen::ImplementCloseBraceGen(const std::shared_ptr<AstObject> &object, int32_t depth)
{
if (!object->IsNode() || ConfigNode::CastFrom(object)->GetNodeType() == NODE_INHERIT) {
return NOERR;
}
if (object == ast_->GetAstRoot()) {
ofs_ << "};\n";
} else {
ofs_ << Indent(depth) << "},\n";
}
return ofs_.good() ? NOERR : EOUTPUT;
}
uint32_t TextGen::ImplementGenTraversal(const std::shared_ptr<AstObject> &object, int32_t depth)
{
if (!object->IsNode() && !object->IsTerm()) {
return NOERR;
}
if (object->IsTerm() && IsInSubClassNode(object)) {
return NOERR;
}
if (object == ast_->GetAstRoot()) {
auto structName = GenConfigStructName(object);
ofs_ << "static const struct " << structName << " " << rootVariableName_ << " = {\n";
if (object->Child() == nullptr) {
ofs_ << "};\n";
}
return ofs_.good() ? NOERR : EOUTPUT;
}
return ObjectImplementGen(object, depth);
}
bool TextGen::IsInSubClassNode(const std::shared_ptr<AstObject> &object)
{
std::shared_ptr<AstObject> obj = object;
while (obj != nullptr) {
if (obj->IsNode() && ConfigNode::CastFrom(obj)->GetNodeType() == NODE_INHERIT) {
return true;
}
obj = obj->Parent();
}
return false;
}
uint32_t TextGen::ObjectImplementGen(const std::shared_ptr<AstObject> &object, int32_t depth)
{
switch (object->Type()) {
case PARSEROP_CONFNODE: {
auto node = ConfigNode::CastFrom(object);
if (node->GetNodeType() != NODE_NOREF) {
return TemplateObjectImplGen(object, depth) ? EASTWALKBREAK : EOUTPUT;
}
ofs_ << Indent(depth) << '.' << node->Name() << " = {\n";
if (node->Child() == nullptr) {
ofs_ << Indent(depth) << "},\n";
}
break;
}
case PARSEROP_CONFTERM:
return PrintTermImplement(object, depth);
default:
return NOERR;
}
return ofs_.good() ? NOERR : EOUTPUT;
}
bool TextGen::TemplateObjectImplGen(const std::shared_ptr<AstObject> &object, int32_t depth)
{
auto node = ConfigNode::CastFrom(object);
if (node->GetNodeType() != NODE_TEMPLATE) {
return true;
}
std::string varName = GenTemplateVariableName(object);
auto nodeName = ToLowerCamelString(node->Name());
ofs_ << Indent(depth) << '.' << nodeName << " = ";
if (node->InheritCount() != 0) {
ofs_ << varName;
} else {
ofs_ << '0';
}
ofs_ << ",\n";
ofs_ << Indent(depth) << '.' << nodeName << "Size = " << std::dec << node->InheritCount() << ",\n";
return ofs_.good();
}
std::string TextGen::GenTemplateVariableName(const std::shared_ptr<AstObject> &object)
{
auto name = ToUpperCamelString(object->Name());
auto sym = SymbolFind(name);
auto node = ConfigNode::CastFrom(object);
if (sym == nullptr) {
SymbolAdd(name, object);
} else if (sym->object != object && node->TemplateSignNum() == 0) {
sym->duplicateCount++;
node->SetTemplateSignNum(sym->duplicateCount);
}
return node->TemplateSignNum() != 0 ?
std::string("g_").append(prefix_).append(name).append(std::to_string(node->TemplateSignNum())) :
std::string("g_").append(prefix_).append(name);
}
std::shared_ptr<TextGen::Symbol> TextGen::SymbolFind(const std::string &name)
{
auto sym = symMap.find(name);
return sym == symMap.end() ? nullptr : sym->second;
}
void TextGen::SymbolAdd(const std::string &name, const std::shared_ptr<AstObject> &object)
{
auto sym = symMap.find(name);
if (sym != symMap.end()) {
sym->second->duplicateCount++;
}
symMap.insert(std::make_pair(std::string(name), std::make_shared<Symbol>(object, 1)));
}
uint32_t TextGen::PrintTermImplement(const std::shared_ptr<AstObject> &object, int32_t depth)
{
auto term = ConfigTerm::CastFrom(object);
auto value = object->Child();
switch (value->Type()) {
case PARSEROP_UINT8:
case PARSEROP_UINT16:
case PARSEROP_UINT32:
case PARSEROP_UINT64:
case PARSEROP_STRING:
ofs_ << Indent(depth) << '.' << term->Name() << " = ";
if (!PrintBaseTypeValue(value)) {
return EOUTPUT;
}
ofs_ << ",\n";
break;
case PARSEROP_ARRAY:
return PrintArrayImplement(object, depth);
case PARSEROP_NODEREF: {
std::string refPath = HcsBuildObjectPath(term->RefNode().lock());
ofs_ << Indent(depth) << '.' << term->Name() << " = &" << refPath << ",\n";
break;
}
default:
break;
}
return ofs_.good() ? NOERR : EOUTPUT;
}
bool TextGen::PrintBaseTypeValue(const std::shared_ptr<AstObject> &object)
{
switch (object->Type()) {
case PARSEROP_UINT8:
case PARSEROP_UINT16:
case PARSEROP_UINT32:
case PARSEROP_UINT64:
ofs_ << "0x" << std::hex << object->IntegerValue();
break;
case PARSEROP_STRING:
ofs_ << '"' << object->StringValue() << '"';
break;
default:
break;
}
return ofs_.good();
}
uint32_t TextGen::PrintArrayImplement(const std::shared_ptr<AstObject> &object, int32_t depth)
{
if (IsInSubClassNode(object)) {
return PrintArrayImplInSubClass(object, depth) ? NOERR : EOUTPUT;
}
auto termContext = object->Child();
ofs_ << Indent(depth) << '.' << object->Name() << " = { ";
if (!HcsPrintArrayContent(termContext, depth + 1)) {
return EOUTPUT;
}
ofs_ << " },\n";
return ofs_.good() ? NOERR : EOUTPUT;
}
bool TextGen::PrintArrayImplInSubClass(const std::shared_ptr<AstObject> &object, int32_t depth)
{
auto array = ConfigArray::CastFrom(object->Child());
auto arrayName = GenArrayName(object);
ofs_ << Indent(depth) << '.' << object->Name() << " = " << arrayName << ",\n";
ofs_ << Indent(depth) << '.' << object->Name() << "Size = " << std::dec << array->ArraySize() << ",\n";
return ofs_.good();
}
bool TextGen::HcsPrintArrayContent(const std::shared_ptr<AstObject> &object, uint32_t indent)
{
constexpr uint32_t ELEMENT_PER_LINE = 16;
auto element = object->Child();
uint32_t elementCount = 0;
while (element != nullptr) {
if (!PrintBaseTypeValue(element)) {
return false;
}
if (elementCount++ >= ELEMENT_PER_LINE) {
ofs_ << "\n" << Indent(indent);
}
element = element->Next();
if (element != nullptr) {
ofs_ << ", ";
}
}
return ofs_.good();
}
std::string TextGen::HcsBuildObjectPath(std::shared_ptr<AstObject> object)
{
std::list<std::shared_ptr<AstObject>> pathList;
auto p = object;
while (p != ast_->GetAstRoot()) {
pathList.push_back(p);
p = p->Parent();
}
pathList.reverse();
std::string path = rootVariableName_;
for (auto &it : pathList) {
path.append(".").append(it->Name());
}
return path;
}
bool TextGen::OutputTemplateImpl()
{
if (!OutputTemplateVariablesDeclare()) {
return false;
}
return ast_->WalkBackward([this](const std::shared_ptr<AstObject> &object, int32_t) -> uint32_t {
if (!object->IsNode() || (object->IsNode() && ConfigNode::CastFrom(object)->GetNodeType() != NODE_TEMPLATE)) {
return NOERR;
}
auto node = ConfigNode::CastFrom(object);
if (node->InheritCount() == 0) {
return NOERR;
}
ofs_ << "static const struct " << GenConfigStructName(object) << ' ' << GenTemplateVariableName(object)
<< "[] = {\n";
auto subClass = node->SubClasses();
for (auto nodeObj : subClass) {
std::shared_ptr<AstObject> obj = std::shared_ptr<AstObject>(nodeObj, [](auto p) {});
ofs_ << Indent() << '[' << std::dec << ConfigNode::CastFrom(obj)->InheritIndex() << "] = {\n";
if (!TemplateVariableGen(obj)) {
return EOUTPUT;
}
ofs_ << Indent() << "},\n";
}
ofs_ << "};\n\n";
return ofs_.good() ? NOERR : EOUTPUT;
});
}
bool TextGen::OutputTemplateVariablesDeclare()
{
return ast_->WalkBackward([this](const std::shared_ptr<AstObject> &object, int32_t) -> uint32_t {
if (object->IsTerm() && object->Child()->IsArray()) {
return ArrayVariablesDeclareGen(object);
} else if (!object->IsNode() ||
(object->IsNode() && ConfigNode::CastFrom(object)->GetNodeType() != NODE_TEMPLATE)) {
return NOERR;
}
auto node = ConfigNode::CastFrom(object);
if (node->InheritCount() == 0) {
return NOERR;
}
auto structName = GenConfigStructName(object);
ofs_ << "static const struct " << GenConfigStructName(object) << ' ' << GenTemplateVariableName(object)
<< "[];\n\n";
return ofs_.good() ? NOERR : EOUTPUT;
});
}
uint32_t TextGen::ArrayVariablesDeclareGen(const std::shared_ptr<AstObject> &object)
{
if (!IsInSubClassNode(object)) {
return NOERR;
}
auto arrayName = GenArrayName(object);
auto array = ConfigArray::CastFrom(object->Child());
ofs_ << "static const " << TypeToStr(array->ArrayType()) << ' ' << arrayName << '[' << std::dec
<< array->ArraySize() << "] = {\n"
<< Indent();
HcsPrintArrayContent(object->Child(), 1);
ofs_ << "\n};\n\n";
return ofs_.good() ? NOERR : EOUTPUT;
}
std::string TextGen::GenArrayName(const std::shared_ptr<AstObject> &term)
{
auto arrayName = std::string("g_hcsConfigArray").append(ToUpperCamelString(term->Name()));
auto t = ConfigTerm::CastFrom(term);
auto sym = SymbolFind(arrayName);
if (sym == nullptr) {
SymbolAdd(arrayName, term);
t->SetSigNum(1);
} else if (t->SigNum() == 0) {
t->SetSigNum(sym->duplicateCount + 1);
sym->duplicateCount++;
}
arrayName.append(std::to_string(t->SigNum()));
return arrayName;
}
uint32_t TextGen::TemplateVariableGen(const std::shared_ptr<AstObject> &nodeObject)
{
auto child = nodeObject->Child();
while (child != nullptr) {
auto res = Ast::WalkRound(
child,
[this](const std::shared_ptr<AstObject> &object, uint32_t depth) -> uint32_t {
return ObjectImplementGen(object, depth + 2);
},
[this](const std::shared_ptr<AstObject> &object, uint32_t depth) -> uint32_t {
return ImplementCloseBraceGen(object, depth + 2);
});
if (!res) {
return false;
}
child = child->Next();
}
return true;
}
bool TextGen::DuplicateNodeNameCheck()
{
std::map<std::string, std::shared_ptr<AstObject>> nodeMap;
return ast_->WalkForward([&](const std::shared_ptr<AstObject> ¤t, uint32_t) -> uint32_t {
if (!current->IsNode() || IsInSubClassNode(current)) {
return NOERR;
}
auto node = nodeMap.find(current->Name());
if (node == nodeMap.end()) {
nodeMap[current->Name()] = current;
return NOERR;
}
Logger().Error() << current->SourceInfo() << "duplicate node name at " << node->second->SourceInfo() << "\n"
<< "To avoid redefining structures, not allow duplicate node name at text config mode";
return EFAIL;
});
}