1 /*
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 *
4 * HDF is dual licensed: you can use it either under the terms of
5 * the GPL, or the BSD license, at your option.
6 * See the LICENSE file in the root of this repository for complete details.
7 */
8
9 #include <algorithm>
10 #include <memory>
11
12 #include "file.h"
13 #include "logger.h"
14 #include "parser.h"
15
16 using namespace OHOS::Hardware;
17
CleanError()18 void Parser::CleanError()
19 {
20 errno_ = NOERR;
21 }
22
Parse()23 bool Parser::Parse()
24 {
25 auto astList = ParseOne(Option::Instance().GetSourceName());
26 if (astList.empty()) {
27 return false;
28 }
29 ast_ = astList.front();
30 astList.pop_front();
31
32 if (!ast_->Merge(astList)) {
33 Logger().Debug() << "failed to merge ast";
34 return false;
35 }
36 ast_->Dump("final merged");
37
38 if (ast_->GetAstRoot() == nullptr) {
39 Logger().Error() << Option::Instance().GetSourceName() << ": Empty hcs file";
40 return false;
41 }
42
43 if (!ast_->Expand()) {
44 return false;
45 }
46
47 return true;
48 }
49
ParseOneContent(const std::string & src,std::list<std::string> & includeList)50 std::shared_ptr<AstObject> Parser::ParseOneContent(const std::string &src, std::list<std::string> &includeList)
51 {
52 if (!lexer_.Initialize(src)) {
53 return nullptr;
54 }
55
56 if (!lexer_.Lex(current_)) {
57 return nullptr;
58 }
59
60 if (current_ == INCLUDE && !ProcessInclude(includeList)) {
61 return nullptr;
62 }
63
64 std::shared_ptr<AstObject> rootNode = nullptr;
65 if (current_ == ROOT) {
66 auto preToken = current_;
67 preToken.type = LITERAL;
68 preToken.strval = "root";
69 rootNode = ParseNode(preToken);
70 if (rootNode == nullptr) {
71 return nullptr;
72 }
73 } else if (current_ != EOF) {
74 Logger().Error() << lexer_ << "syntax error, expect root node of end of file";
75 return nullptr;
76 } else if (current_ == EOF && includeList.empty()) {
77 Logger().Error() << src << ": Empty hcs file";
78 return nullptr;
79 }
80
81 if (!lexer_.Lex(current_) || current_ != EOF) {
82 Logger().Error() << lexer_ << "syntax error, expect EOF";
83 return nullptr;
84 }
85
86 return rootNode;
87 }
88
ParseOne(const std::string & src)89 std::list<std::shared_ptr<Ast>> Parser::ParseOne(const std::string &src)
90 {
91 srcQueue_.push_back(src);
92
93 std::list<std::shared_ptr<Ast>> astList;
94 std::list<std::string> includeList;
95 CleanError();
96
97 std::shared_ptr<AstObject> rootNode = ParseOneContent(src, includeList);
98 /* hcs allows a file have only include list, but does not allow empty files */
99 if ((rootNode == nullptr && includeList.empty()) || errno_ != NOERR) {
100 return astList;
101 }
102
103 for (auto includeSrc : includeList) {
104 if (!CheckCycleInclude(includeSrc)) {
105 Logger().Error() << src << " circular include " << includeSrc;
106 return std::list<std::shared_ptr<Ast>>();
107 }
108 auto includeAstList = ParseOne(includeSrc);
109 if (includeAstList.empty()) {
110 return std::list<std::shared_ptr<Ast>>();
111 }
112 astList.splice(astList.end(), includeAstList);
113 }
114 srcQueue_.pop_back();
115 auto oneAst = std::make_shared<Ast>(rootNode);
116 oneAst->Dump(src);
117 astList.emplace_back(oneAst);
118 return astList;
119 }
120
CheckCycleInclude(const std::string & includeSrc)121 bool Parser::CheckCycleInclude(const std::string &includeSrc)
122 {
123 return std::find(std::begin(srcQueue_), std::end(srcQueue_), includeSrc) == std::end(srcQueue_);
124 }
125
ProcessInclude(std::list<std::string> & includeList)126 bool Parser::ProcessInclude(std::list<std::string> &includeList)
127 {
128 do {
129 if (!lexer_.Lex(current_) || current_ != STRING) {
130 Logger().Error() << lexer_ << "syntax error, expect include path after ’#include‘";
131 errno_ = EFAIL;
132 return false;
133 }
134
135 auto includePath = current_.strval;
136 if (includePath.empty()) {
137 Logger().Error() << lexer_ << "include invalid file: \'" << includePath << '\'';
138 errno_ = EFAIL;
139 return false;
140 }
141 if (includePath[0] != '/') {
142 auto currentSrc = srcQueue_.back();
143 auto currentSrcDir = Util::File::GetDir(currentSrc);
144 includePath = currentSrcDir.append(includePath);
145 }
146
147 auto includeAbsPath = Util::File::AbsPath(includePath);
148 if (includeAbsPath.empty()) {
149 Logger().Error() << lexer_ << "include invalid file: \'" << current_.strval << '\'';
150 errno_ = EFAIL;
151 return false;
152 }
153
154 includeList.push_back(includeAbsPath);
155
156 if (!lexer_.Lex(current_)) {
157 return false;
158 }
159
160 if (current_ == INCLUDE) {
161 continue;
162 }
163
164 break;
165 } while (true);
166
167 return true;
168 }
169
ParseNode(Token & name,bool bracesStart)170 std::shared_ptr<AstObject> Parser::ParseNode(Token &name, bool bracesStart)
171 {
172 /* bracesStart: if true, current is '{' , else need to read next token and check with '}' */
173 if (!bracesStart) {
174 if (!lexer_.Lex(current_) || current_ != '{') {
175 Logger().Error() << lexer_ << "syntax error, node miss '{'";
176 return nullptr;
177 }
178 }
179
180 auto node = std::make_shared<ConfigNode>(name, NODE_NOREF, "");
181 std::shared_ptr<AstObject> child;
182 while (lexer_.Lex(current_) && current_ != '}') {
183 switch (current_.type) {
184 case TEMPLATE:
185 child = ParseTemplate();
186 break;
187 case LITERAL:
188 child = ParseNodeAndTerm();
189 break;
190 default:
191 Logger().Error() << lexer_ << "syntax error, except '}' or TEMPLATE or LITERAL for node '"
192 << name.strval << '\'';
193 return nullptr;
194 }
195 if (child == nullptr) {
196 return nullptr;
197 }
198
199 node->AddChild(child);
200 }
201
202 if (current_ != '}') {
203 Logger().Error() << lexer_ << "syntax error, node miss '}'";
204 return nullptr;
205 }
206 return node;
207 }
208
ParseTerm(Token & name)209 std::shared_ptr<AstObject> Parser::ParseTerm(Token &name)
210 {
211 if (!lexer_.Lex(current_)) {
212 Logger().Error() << lexer_ << "syntax error, miss value of config term";
213 return nullptr;
214 }
215 auto term = std::make_shared<ConfigTerm>(name, nullptr);
216 switch (current_.type) {
217 case STRING:
218 term->AddChild(std::make_shared<AstObject>("", PARSEROP_STRING, current_.strval, current_));
219 break;
220 case NUMBER:
221 term->AddChild(std::make_shared<AstObject>("", PARSEROP_UINT64, current_.numval, current_));
222 break;
223 case '[': {
224 std::shared_ptr<AstObject> list = ParseArray();
225 if (list == nullptr) {
226 return nullptr;
227 } else {
228 term->AddChild(list);
229 }
230 break;
231 }
232 case '&':
233 if (!lexer_.Lex(current_) || (current_ != LITERAL && current_ != REF_PATH)) {
234 Logger().Error() << lexer_ << "syntax error, invalid config term definition";
235 return nullptr;
236 }
237 term->AddChild(std::make_shared<AstObject>("", PARSEROP_NODEREF, current_.strval, current_));
238 break;
239 case DELETE:
240 term->AddChild(std::make_shared<AstObject>("", PARSEROP_DELETE, current_.strval, current_));
241 break;
242 default:
243 Logger().Error() << lexer_ << "syntax error, invalid config term definition";
244 return nullptr;
245 }
246
247 if (!lexer_.Lex(current_) || current_ != ';') {
248 Logger().Error() << lexer_ << "syntax error, miss ';'";
249 return nullptr;
250 }
251
252 return term;
253 }
254
ParseTemplate()255 std::shared_ptr<AstObject> Parser::ParseTemplate()
256 {
257 if (!lexer_.Lex(current_) || current_ != LITERAL) {
258 Logger().Error() << lexer_ << "syntax error, template miss name";
259 return nullptr;
260 }
261
262 auto name = current_;
263 auto node = ParseNode(name, false);
264 if (node == nullptr) {
265 return node;
266 }
267
268 ConfigNode::CastFrom(node)->SetNodeType(NODE_TEMPLATE);
269 return node;
270 }
271
ParseNodeAndTerm()272 std::shared_ptr<AstObject> Parser::ParseNodeAndTerm()
273 {
274 auto name = current_;
275 if (!lexer_.Lex(current_)) {
276 Logger().Error() << lexer_ << "syntax error, broken term or node";
277 return nullptr;
278 }
279
280 switch (current_.type) {
281 case '=':
282 return ParseTerm(name);
283 case '{':
284 return ParseNode(name, true);
285 case ':':
286 if (lexer_.Lex(current_)) {
287 return ParseNodeWithRef(name);
288 }
289 Logger().Error() << lexer_ << "syntax error, unknown node reference type";
290 break;
291 default:
292 Logger().Error() << lexer_ << "syntax error, except '=' or '{' or ':'";
293 break;
294 }
295
296 return nullptr;
297 }
298
ParseNodeWithRef(Token name)299 std::shared_ptr<AstObject> Parser::ParseNodeWithRef(Token name)
300 {
301 std::shared_ptr<AstObject> node;
302 switch (current_.type) {
303 case REF_PATH:
304 case LITERAL:
305 return ParseNodeCopy(name);
306 case '&':
307 return ParseNodeRef(name);
308 case DELETE:
309 return ParseNodeDelete(name);
310 case ':':
311 return ParseNodeInherit(name);
312 default:
313 Logger().Error() << lexer_ << "syntax error, unknown node type";
314 break;
315 }
316
317 return node;
318 }
319
320 /* started with NodePath on gramme : LITERAL ':' NodePath '{' ConfigTermList '}' */
ParseNodeCopy(Token & name)321 std::shared_ptr<AstObject> Parser::ParseNodeCopy(Token &name)
322 {
323 auto nodePath = current_.strval;
324
325 auto node = ParseNode(name);
326 if (node == nullptr) {
327 return nullptr;
328 }
329
330 auto nodeCopy = ConfigNode::CastFrom(node);
331 nodeCopy->SetNodeType(NODE_COPY);
332 nodeCopy->SetRefPath(nodePath);
333
334 return node;
335 }
336
337 /* started with & on gramme : LITERAL ':' '&' NodePath '{' ConfigTermList '}' */
ParseNodeRef(Token & name)338 std::shared_ptr<AstObject> Parser::ParseNodeRef(Token &name)
339 {
340 if (!lexer_.Lex(current_) || (current_ != LITERAL && current_ != REF_PATH)) {
341 Logger().Error() << lexer_ << "syntax error, miss node reference path";
342 return nullptr;
343 }
344 auto refPath = current_.strval;
345 auto node = ParseNode(name);
346 if (node == nullptr) {
347 return nullptr;
348 }
349
350 auto configNode = ConfigNode::CastFrom(node);
351 configNode->SetNodeType(NODE_REF);
352 configNode->SetRefPath(refPath);
353 return node;
354 }
355
356 /* started with DELETE on gramme : LITERAL ':' DELETE '{' ConfigTermList '}' */
ParseNodeDelete(Token & name)357 std::shared_ptr<AstObject> Parser::ParseNodeDelete(Token &name)
358 {
359 auto node = ParseNode(name);
360 if (node == nullptr) {
361 return nullptr;
362 }
363
364 /* maybe drop node context is better */
365 auto configNode = ConfigNode::CastFrom(node);
366 configNode->SetNodeType(NODE_DELETE);
367 return node;
368 }
369
370 /* started with 2th ':' on gramme : LITERAL ':' ':' NodePath '{' ConfigTermList '}' */
ParseNodeInherit(Token & name)371 std::shared_ptr<AstObject> Parser::ParseNodeInherit(Token &name)
372 {
373 if (!lexer_.Lex(current_) || (current_ != LITERAL && current_ != REF_PATH)) {
374 Logger().Error() << lexer_ << "syntax error, miss node inherit path";
375 return nullptr;
376 }
377
378 auto inheritPath = current_.strval;
379
380 auto node = ParseNode(name);
381 if (node == nullptr) {
382 return nullptr;
383 }
384
385 auto configNode = ConfigNode::CastFrom(node);
386 configNode->SetNodeType(NODE_INHERIT);
387 configNode->SetRefPath(inheritPath);
388
389 return node;
390 }
391
ParseArray()392 std::shared_ptr<AstObject> Parser::ParseArray()
393 {
394 auto array = std::make_shared<ConfigArray>(current_);
395 int32_t arrayType = 0;
396
397 while (lexer_.Lex(current_) && current_ != ']') {
398 if (current_.type == STRING) {
399 array->AddChild(std::make_shared<AstObject>("", PARSEROP_STRING, current_.strval, current_));
400 } else if (current_.type == NUMBER) {
401 array->AddChild(std::make_shared<AstObject>("", PARSEROP_UINT64, current_.numval, current_));
402 } else {
403 Logger().Error() << lexer_ << "syntax error, except STRING or NUMBER in array";
404 return nullptr;
405 }
406
407 if (arrayType == 0) {
408 arrayType = current_.type;
409 } else if (arrayType != current_.type) {
410 Logger().Error() << lexer_ << "syntax error, not allow mix type array";
411 return nullptr;
412 }
413
414 if (lexer_.Lex(current_)) {
415 if (current_ == ',') {
416 continue;
417 } else if (current_ == ']') {
418 break;
419 } else {
420 Logger().Error() << lexer_ << "syntax error, except ',' or ']'";
421 return nullptr;
422 }
423 }
424 return std::shared_ptr<AstObject>();
425 }
426
427 if (current_ != ']') {
428 Logger().Error() << lexer_ << "syntax error, miss ']' at end of array";
429 return nullptr;
430 }
431
432 return array;
433 }
434
GetAst()435 std::shared_ptr<Ast> Parser::GetAst()
436 {
437 return ast_;
438 }
439