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