/* * 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 #include #include "file.h" #include "logger.h" #include "parser.h" using namespace OHOS::Hardware; 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 Parser::ParseOneContent(const std::string &src, std::list &includeList) { if (!lexer_.Initialize(src)) { return nullptr; } if (!lexer_.Lex(current_)) { return nullptr; } if (current_ == INCLUDE && !ProcessInclude(includeList)) { return nullptr; } std::shared_ptr 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> Parser::ParseOne(const std::string &src) { srcQueue_.push_back(src); std::list> astList; std::list includeList; CleanError(); std::shared_ptr rootNode = ParseOneContent(src, includeList); /* hcs allows a file have only include list, but does not allow empty files */ 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>(); } auto includeAstList = ParseOne(includeSrc); if (includeAstList.empty()) { return std::list>(); } astList.splice(astList.end(), includeAstList); } srcQueue_.pop_back(); auto oneAst = std::make_shared(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 &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 Parser::ParseNode(Token &name, bool bracesStart) { /* bracesStart: if true, current is '{' , else need to read next token and check with '}' */ if (!bracesStart) { if (!lexer_.Lex(current_) || current_ != '{') { Logger().Error() << lexer_ << "syntax error, node miss '{'"; return nullptr; } } auto node = std::make_shared(name, NODE_NOREF, ""); std::shared_ptr 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 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(name, nullptr); switch (current_.type) { case STRING: term->AddChild(std::make_shared("", PARSEROP_STRING, current_.strval, current_)); break; case NUMBER: term->AddChild(std::make_shared("", PARSEROP_UINT64, current_.numval, current_)); break; case '[': { std::shared_ptr 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("", PARSEROP_NODEREF, current_.strval, current_)); break; case DELETE: term->AddChild(std::make_shared("", 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 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 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 Parser::ParseNodeWithRef(Token name) { std::shared_ptr 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; } /* started with NodePath on gramme : LITERAL ':' NodePath '{' ConfigTermList '}' */ std::shared_ptr 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; } /* started with & on gramme : LITERAL ':' '&' NodePath '{' ConfigTermList '}' */ std::shared_ptr 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; } /* started with DELETE on gramme : LITERAL ':' DELETE '{' ConfigTermList '}' */ std::shared_ptr Parser::ParseNodeDelete(Token &name) { auto node = ParseNode(name); if (node == nullptr) { return nullptr; } /* maybe drop node context is better */ auto configNode = ConfigNode::CastFrom(node); configNode->SetNodeType(NODE_DELETE); return node; } /* started with 2th ':' on gramme : LITERAL ':' ':' NodePath '{' ConfigTermList '}' */ std::shared_ptr 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 Parser::ParseArray() { auto array = std::make_shared(current_); int32_t arrayType = 0; while (lexer_.Lex(current_) && current_ != ']') { if (current_.type == STRING) { array->AddChild(std::make_shared("", PARSEROP_STRING, current_.strval, current_)); } else if (current_.type == NUMBER) { array->AddChild(std::make_shared("", 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(); } if (current_ != ']') { Logger().Error() << lexer_ << "syntax error, miss ']' at end of array"; return nullptr; } return array; } std::shared_ptr Parser::GetAst() { return ast_; }