* 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 <algorithm>
#include <memory>
#include "file.h"
#include "logger.h"
#include "parser.h"
using namespace OHOS::Hardware;
Parser::Parser() : errno_(NOERR), current_() {}
void Parser::CleanError()
{
errno_ = NOERR;
}
bool Parser::Parse()
{
auto astList = ParseOne(Option::Instance().GetSourceName());
if (astList.empty()) {
return false;
}
ast_ = astList.front();
astList.pop_front();
if (!ast_->Merge(astList)) {
Logger().Debug() << "failed to merge ast";
return false;
}
ast_->Dump("final merged");
if (ast_->GetAstRoot() == nullptr) {
Logger().Error() << Option::Instance().GetSourceName() << ": Empty hcs file";
return false;
}
if (!ast_->Expand()) {
return false;
}
return true;
}
std::shared_ptr<AstObject> Parser::ParseOneContent(const std::string &src, std::list<std::string> &includeList)
{
if (!lexer_.Initialize(src)) {
return nullptr;
}
if (!lexer_.Lex(current_)) {
return nullptr;
}
if (current_ == INCLUDE && !ProcessInclude(includeList)) {
return nullptr;
}
std::shared_ptr<AstObject> rootNode = nullptr;
if (current_ == ROOT) {
auto preToken = current_;
preToken.type = LITERAL;
preToken.strval = "root";
rootNode = ParseNode(preToken);
if (rootNode == nullptr) {
return nullptr;
}
} else if (current_ != EOF) {
Logger().Error() << lexer_ << "syntax error, expect root node of end of file";
return nullptr;
} else if (current_ == EOF && includeList.empty()) {
Logger().Error() << src << ": Empty hcs file";
return nullptr;
}
if (!lexer_.Lex(current_) || current_ != EOF) {
Logger().Error() << lexer_ << "syntax error, expect EOF";
return nullptr;
}
return rootNode;
}
std::list<std::shared_ptr<Ast>> Parser::ParseOne(const std::string &src)
{
srcQueue_.push_back(src);
std::list<std::shared_ptr<Ast>> astList;
std::list<std::string> includeList;
CleanError();
std::shared_ptr<AstObject> rootNode = ParseOneContent(src, includeList);
if ((rootNode == nullptr && includeList.empty()) || errno_ != NOERR) {
return astList;
}
for (auto includeSrc : includeList) {
if (!CheckCycleInclude(includeSrc)) {
Logger().Error() << src << " circular include " << includeSrc;
return std::list<std::shared_ptr<Ast>>();
}
auto includeAstList = ParseOne(includeSrc);
if (includeAstList.empty()) {
return std::list<std::shared_ptr<Ast>>();
}
astList.splice(astList.end(), includeAstList);
}
srcQueue_.pop_back();
auto oneAst = std::make_shared<Ast>(rootNode);
oneAst->Dump(src);
astList.emplace_back(oneAst);
return astList;
}
bool Parser::CheckCycleInclude(const std::string &includeSrc)
{
return std::find(std::begin(srcQueue_), std::end(srcQueue_), includeSrc) == std::end(srcQueue_);
}
bool Parser::ProcessInclude(std::list<std::string> &includeList)
{
do {
if (!lexer_.Lex(current_) || current_ != STRING) {
Logger().Error() << lexer_ << "syntax error, expect include path after ’#include‘";
errno_ = EFAIL;
return false;
}
auto includePath = current_.strval;
if (includePath.empty()) {
Logger().Error() << lexer_ << "include invalid file: \'" << includePath << '\'';
errno_ = EFAIL;
return false;
}
if (includePath[0] != '/') {
auto currentSrc = srcQueue_.back();
auto currentSrcDir = Util::File::GetDir(currentSrc);
includePath = currentSrcDir.append(includePath);
}
auto includeAbsPath = Util::File::AbsPath(includePath);
if (includeAbsPath.empty()) {
Logger().Error() << lexer_ << "include invalid file: \'" << current_.strval << '\'';
errno_ = EFAIL;
return false;
}
includeList.push_back(includeAbsPath);
if (!lexer_.Lex(current_)) {
return false;
}
if (current_ == INCLUDE) {
continue;
}
break;
} while (true);
return true;
}
std::shared_ptr<AstObject> Parser::ParseNode(Token &name, bool bracesStart)
{
if (!bracesStart) {
if (!lexer_.Lex(current_) || current_ != '{') {
Logger().Error() << lexer_ << "syntax error, node miss '{'";
return nullptr;
}
}
auto node = std::make_shared<ConfigNode>(name, NODE_NOREF, "");
std::shared_ptr<AstObject> child;
while (lexer_.Lex(current_) && current_ != '}') {
switch (current_.type) {
case TEMPLATE:
child = ParseTemplate();
break;
case LITERAL:
child = ParseNodeAndTerm();
break;
default:
Logger().Error() << lexer_ << "syntax error, except '}' or TEMPLATE or LITERAL for node '"
<< name.strval << '\'';
return nullptr;
}
if (child == nullptr) {
return nullptr;
}
node->AddChild(child);
}
if (current_ != '}') {
Logger().Error() << lexer_ << "syntax error, node miss '}'";
return nullptr;
}
return node;
}
std::shared_ptr<AstObject> Parser::ParseTerm(Token &name)
{
if (!lexer_.Lex(current_)) {
Logger().Error() << lexer_ << "syntax error, miss value of config term";
return nullptr;
}
auto term = std::make_shared<ConfigTerm>(name, nullptr);
switch (current_.type) {
case STRING:
term->AddChild(std::make_shared<AstObject>("", PARSEROP_STRING, current_.strval, current_));
break;
case NUMBER:
term->AddChild(std::make_shared<AstObject>("", PARSEROP_UINT64, current_.numval, current_));
break;
case '[': {
std::shared_ptr<AstObject> list = ParseArray();
if (list == nullptr) {
return nullptr;
} else {
term->AddChild(list);
}
break;
}
case '&':
if (!lexer_.Lex(current_) || (current_ != LITERAL && current_ != REF_PATH)) {
Logger().Error() << lexer_ << "syntax error, invalid config term definition";
return nullptr;
}
term->AddChild(std::make_shared<AstObject>("", PARSEROP_NODEREF, current_.strval, current_));
break;
case DELETE:
term->AddChild(std::make_shared<AstObject>("", PARSEROP_DELETE, current_.strval, current_));
break;
default:
Logger().Error() << lexer_ << "syntax error, invalid config term definition";
return nullptr;
}
if (!lexer_.Lex(current_) || current_ != ';') {
Logger().Error() << lexer_ << "syntax error, miss ';'";
return nullptr;
}
return term;
}
std::shared_ptr<AstObject> Parser::ParseTemplate()
{
if (!lexer_.Lex(current_) || current_ != LITERAL) {
Logger().Error() << lexer_ << "syntax error, template miss name";
return nullptr;
}
auto name = current_;
auto node = ParseNode(name, false);
if (node == nullptr) {
return node;
}
ConfigNode::CastFrom(node)->SetNodeType(NODE_TEMPLATE);
return node;
}
std::shared_ptr<AstObject> Parser::ParseNodeAndTerm()
{
auto name = current_;
if (!lexer_.Lex(current_)) {
Logger().Error() << lexer_ << "syntax error, broken term or node";
return nullptr;
}
switch (current_.type) {
case '=':
return ParseTerm(name);
case '{':
return ParseNode(name, true);
case ':':
if (lexer_.Lex(current_)) {
return ParseNodeWithRef(name);
}
Logger().Error() << lexer_ << "syntax error, unknown node reference type";
break;
default:
Logger().Error() << lexer_ << "syntax error, except '=' or '{' or ':'";
break;
}
return nullptr;
}
std::shared_ptr<AstObject> Parser::ParseNodeWithRef(Token name)
{
std::shared_ptr<AstObject> node;
switch (current_.type) {
case REF_PATH:
case LITERAL:
return ParseNodeCopy(name);
case '&':
return ParseNodeRef(name);
case DELETE:
return ParseNodeDelete(name);
case ':':
return ParseNodeInherit(name);
default:
Logger().Error() << lexer_ << "syntax error, unknown node type";
break;
}
return node;
}
std::shared_ptr<AstObject> Parser::ParseNodeCopy(Token &name)
{
auto nodePath = current_.strval;
auto node = ParseNode(name);
if (node == nullptr) {
return nullptr;
}
auto nodeCopy = ConfigNode::CastFrom(node);
nodeCopy->SetNodeType(NODE_COPY);
nodeCopy->SetRefPath(nodePath);
return node;
}
std::shared_ptr<AstObject> Parser::ParseNodeRef(Token &name)
{
if (!lexer_.Lex(current_) || (current_ != LITERAL && current_ != REF_PATH)) {
Logger().Error() << lexer_ << "syntax error, miss node reference path";
return nullptr;
}
auto refPath = current_.strval;
auto node = ParseNode(name);
if (node == nullptr) {
return nullptr;
}
auto configNode = ConfigNode::CastFrom(node);
configNode->SetNodeType(NODE_REF);
configNode->SetRefPath(refPath);
return node;
}
std::shared_ptr<AstObject> Parser::ParseNodeDelete(Token &name)
{
auto node = ParseNode(name);
if (node == nullptr) {
return nullptr;
}
auto configNode = ConfigNode::CastFrom(node);
configNode->SetNodeType(NODE_DELETE);
return node;
}
std::shared_ptr<AstObject> Parser::ParseNodeInherit(Token &name)
{
if (!lexer_.Lex(current_) || (current_ != LITERAL && current_ != REF_PATH)) {
Logger().Error() << lexer_ << "syntax error, miss node inherit path";
return nullptr;
}
auto inheritPath = current_.strval;
auto node = ParseNode(name);
if (node == nullptr) {
return nullptr;
}
auto configNode = ConfigNode::CastFrom(node);
configNode->SetNodeType(NODE_INHERIT);
configNode->SetRefPath(inheritPath);
return node;
}
std::shared_ptr<AstObject> Parser::ParseArray()
{
auto array = std::make_shared<ConfigArray>(current_);
int32_t arrayType = 0;
while (lexer_.Lex(current_) && current_ != ']') {
if (current_.type == STRING) {
array->AddChild(std::make_shared<AstObject>("", PARSEROP_STRING, current_.strval, current_));
} else if (current_.type == NUMBER) {
array->AddChild(std::make_shared<AstObject>("", PARSEROP_UINT64, current_.numval, current_));
} else {
Logger().Error() << lexer_ << "syntax error, except STRING or NUMBER in array";
return nullptr;
}
if (arrayType == 0) {
arrayType = current_.type;
} else if (arrayType != current_.type) {
Logger().Error() << lexer_ << "syntax error, not allow mix type array";
return nullptr;
}
if (lexer_.Lex(current_)) {
if (current_ == ',') {
continue;
} else if (current_ == ']') {
break;
} else {
Logger().Error() << lexer_ << "syntax error, except ',' or ']'";
return nullptr;
}
}
return std::shared_ptr<AstObject>();
}
if (current_ != ']') {
Logger().Error() << lexer_ << "syntax error, miss ']' at end of array";
return nullptr;
}
return array;
}
std::shared_ptr<Ast> Parser::GetAst()
{
return ast_;
}