• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2022-2023 Shenzhen Kaihong Digital Industry Development Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16const { NapiLog } = require('./NapiLog');
17const { Lexer, TokenType, code } = require('./lexer');
18const { XMessage } = require('../message/XMessage');
19const {
20  AstObject,
21  ConfigNode,
22  ConfigTerm,
23  ConfigArray,
24  NodeRefType,
25  ObjectType,
26  Ast,
27} = require('./ast');
28
29function copy(obj) {
30  return JSON.parse(JSON.stringify(obj));
31}
32class Parser {
33  constructor() {
34    this.lexer_ = new Lexer();
35    this.current_ = {};
36    this.srcQueue_ = [];
37    this.astList = {};
38  }
39  parse(fn) {
40    if (this.srcQueue_.indexOf(fn) === -1) {
41      this.srcQueue_.push(fn);
42    }
43
44    while (this.srcQueue_.length > 0) {
45      let includeList = [];
46      let oneAst = this.parseOne(this.srcQueue_[0], includeList);
47      if (oneAst === null) {
48        return false;
49      }
50
51      this.astList[this.srcQueue_[0]] = {
52        ast: oneAst,
53        include: includeList,
54      };
55      this.srcQueue_.shift();
56      this.srcQueue_ = this.srcQueue_.concat(includeList);
57    }
58    return this.astList;
59  }
60  convertAbsPath(includeBy, includePath) {
61    if (navigator.userAgent.toLowerCase().indexOf('win') > -1) {
62      //windows
63      if (includePath[1] !== ':') {
64        let currentSrc = includeBy.substring(
65          0,
66          includeBy.lastIndexOf('\\') + 1
67        );
68        includePath = currentSrc + includePath.replace(/\//g, '\\');
69
70        includePath = includePath.replace(/\\\.\\/g, '\\');
71      }
72    } else if (includePath[0] !== '/') {
73      let currentSrc = includeBy.substring(0, includeBy.lastIndexOf('/') + 1);
74      includePath = currentSrc + includePath;
75    }
76    return includePath;
77  }
78  processInclude(includeList) {
79    do {
80      if (
81        !this.lexer_.lex(this.current_) ||
82        this.current_.type !== TokenType.STRING
83      ) {
84        this.dealWithError(
85          this.lexer_ + 'syntax error, expect include path after ’#include'
86        );
87        return false;
88      }
89
90      let includePath = this.current_.strval;
91      if (includePath.length <= 0) {
92        this.dealWithError(
93          this.lexer_ + 'include invalid file:' + this.includePath
94        );
95        return false;
96      }
97      includePath = this.convertAbsPath(this.srcQueue_[0], includePath);
98
99      includeList.push(includePath);
100
101      if (!this.lexer_.lex(this.current_)) {
102        return false;
103      }
104
105      if (this.current_.type !== TokenType.INCLUDE) {
106        break;
107      }
108    } while (true);
109
110    return true;
111  }
112  parseOne(src, includeList) {
113    if (!this.lexer_.initialize(src)) {
114      return null;
115    }
116
117    if (!this.lexer_.lex(this.current_)) {
118      return null;
119    }
120
121    if (
122      this.current_.type === TokenType.INCLUDE &&
123      !this.processInclude(includeList)
124    ) {
125      return null;
126    }
127
128    let rootNode = null;
129    if (this.current_.type === TokenType.ROOT) {
130      let preToken = copy(this.current_);
131      preToken.type = TokenType.LITERAL;
132      preToken.strval = 'root';
133      rootNode = this.parseNode(preToken);
134      if (rootNode === null) {
135        return null;
136      }
137    } else if (this.current_.type !== TokenType.EOF) {
138      this.dealWithError('syntax error, expect root node of end of file');
139      return null;
140    }
141
142    if (
143      !this.lexer_.lex(this.current_) ||
144      this.current_.type !== TokenType.EOF
145    ) {
146      this.dealWithError('syntax error, expect EOF');
147      return null;
148    }
149
150    let oneAst = new Ast(rootNode);
151    return oneAst;
152  }
153
154  parseNode(name, bracesStart) {
155    if (!bracesStart) {
156      if (!this.lexer_.lex(this.current_) || this.current_.type !== code('{')) {
157        this.dealWithError("syntax error, node miss '{'");
158        return null;
159      }
160    }
161
162    let node = new ConfigNode(name, NodeRefType.NODE_NOREF, '');
163    let child;
164    while (this.lexer_.lex(this.current_) && this.current_.type !== code('}')) {
165      switch (this.current_.type) {
166        case TokenType.TEMPLATE:
167          child = this.parseTemplate();
168          break;
169        case TokenType.LITERAL:
170          child = this.parseNodeAndTerm();
171          break;
172        default:
173          this.dealWithError(
174            "syntax error, except '}' or TEMPLATE or LITERAL for node '" +
175              name.strval +
176              ' at lineNo=' +
177              child.lineno_ +
178              "'"
179          );
180          return null;
181      }
182      if (child === null) {
183        return null;
184      }
185
186      node.addChild(child);
187    }
188
189    if (this.current_.type !== code('}')) {
190      this.dealWithError(this.lexer_ + "syntax error, node miss '}'");
191      return null;
192    }
193    return node;
194  }
195
196  parseNodeAndTerm() {
197    let name = copy(this.current_);
198    if (!this.lexer_.lex(this.current_)) {
199      this.dealWithError(this.lexer_ + 'syntax error, broken term or node');
200      return null;
201    }
202
203    switch (this.current_.type) {
204      case code('='):
205        return this.parseTerm(name);
206      case code('{'):
207        return this.parseNode(name, true);
208      case code(':'):
209        if (this.lexer_.lex(this.current_)) {
210          return this.parseNodeWithRef(name);
211        }
212        this.dealWithError('syntax error, unknown node reference type');
213        break;
214      default:
215        this.dealWithError("syntax error, except '=' or '{' or ':'");
216        break;
217    }
218
219    return null;
220  }
221  parseArray() {
222    let array = new ConfigArray(this.current_);
223    let arrayType = 0;
224
225    while (this.lexer_.lex(this.current_) && this.current_.type !== code(']')) {
226      if (this.current_.type === TokenType.STRING) {
227        array.addChild(new AstObject('', ObjectType.PARSEROP_STRING, this.current_.strval, this.current_));
228      } else if (this.current_.type === TokenType.NUMBER) {
229        array.addChild(new AstObject('', ObjectType.PARSEROP_UINT64,
230            this.current_.numval, this.current_, this.current_.baseSystem));
231      } else {
232        this.dealWithError(this.lexer_ + 'syntax error, except STRING or NUMBER in array');
233        return nullptr;
234      }
235
236      if (arrayType === 0) {
237        arrayType = this.current_.type;
238      } else if (arrayType !== this.current_.type) {
239        this.dealWithError(this.lexer_ + 'syntax error, not allow mix type array');
240        return null;
241      }
242
243      if (this.lexer_.lex(this.current_)) {
244        if (this.current_.type === code(']')) {
245          break;
246        } else if (this.current_.type !== code(',')) {
247          this.dealWithError(this.lexer_ + "syntax error, except ',' or ']'");
248          return null;
249        }
250      } else { return null; }
251    }
252    if (this.current_.type !== code(']')) {
253      this.dealWithError(this.lexer_ + "syntax error, miss ']' at end of array");
254      return null;
255    }
256    return array;
257  }
258  parseTerm(name) {
259    if (!this.lexer_.lex(this.current_)) {
260      this.dealWithError('syntax error, miss value of config term');
261      return null;
262    }
263    let term = new ConfigTerm(name, null);
264    switch (this.current_.type) {
265      case TokenType.BOOL:
266        term.addChild(new AstObject('', ObjectType.PARSEROP_BOOL, this.current_.numval, this.current_));
267        break;
268      case TokenType.STRING:
269        term.addChild(new AstObject('', ObjectType.PARSEROP_STRING, this.current_.strval, this.current_));
270        break;
271      case TokenType.NUMBER:
272        term.addChild(new AstObject('', ObjectType.PARSEROP_UINT64, this.current_.numval, this.current_, this.current_.baseSystem));
273        break;
274      case code('['): {
275        let list = this.parseArray();
276        if (list === null) {
277          return null;
278        } else {
279          term.addChild(list);
280        }
281        break;
282      }
283      case code('&'):
284        if (!this.lexer_.lex(this.current_) || (this.current_.type !== TokenType.LITERAL && this.current_.type !== TokenType.REF_PATH)) {
285          this.dealWithError('syntax error, invalid config term definition');
286          return null;
287        }
288        term.addChild(new AstObject('', ObjectType.PARSEROP_NODEREF, this.current_.strval, this.current_));
289        break;
290      case TokenType.DELETE:
291        term.addChild(new AstObject('', ObjectType.PARSEROP_DELETE, this.current_.strval, this.current_));
292        break;
293      default:
294        this.dealWithError('syntax error, invalid config term definition');
295        return null;
296    }
297
298    if (!this.lexer_.lex(this.current_) || this.current_.type !== code(';')) {
299      this.dealWithError("syntax error, miss ';'");
300      return null;
301    }
302
303    return term;
304  }
305  parseTemplate() {
306    if (
307      !this.lexer_.lex(this.current_) ||
308      this.current_.type !== TokenType.LITERAL
309    ) {
310      this.dealWithError('syntax error, template miss name');
311      return null;
312    }
313    let name = copy(this.current_);
314    let node = this.parseNode(name, false);
315    if (node === null) {
316      return node;
317    }
318
319    node.setNodeType(NodeRefType.NODE_TEMPLATE);
320    return node;
321  }
322
323  parseNodeRef(name) {
324    if (
325      !this.lexer_.lex(this.current_) ||
326      (this.current_.type !== TokenType.LITERAL &&
327        this.current_.type !== TokenType.REF_PATH)
328    ) {
329      this.dealWithError(
330        this.lexer_ + 'syntax error, miss node reference path'
331      );
332      return nullptr;
333    }
334    let refPath = this.current_.strval;
335    let node = this.parseNode(name);
336    if (node === null) {
337      return null;
338    }
339
340    let configNode = node;
341    configNode.setNodeType(NodeRefType.NODE_REF);
342    configNode.setRefPath(refPath);
343    return node;
344  }
345  parseNodeDelete(name) {
346    let node = this.parseNode(name);
347    if (node === null) {
348      return null;
349    }
350
351    node.setNodeType(NodeRefType.NODE_DELETE);
352    return node;
353  }
354  parseNodeCopy(name) {
355    let nodePath = this.current_.strval;
356
357    let node = this.parseNode(name);
358    if (node === null) {
359      return null;
360    }
361
362    let nodeCopy = node;
363    nodeCopy.setNodeType(NodeRefType.NODE_COPY);
364    nodeCopy.setRefPath(nodePath);
365
366    return node;
367  }
368  parseNodeWithRef(name) {
369    switch (this.current_.type) {
370      case TokenType.REF_PATH:
371      case TokenType.LITERAL:
372        return this.parseNodeCopy(name);
373      case code('&'):
374        return this.parseNodeRef(name);
375      case TokenType.DELETE:
376        return this.parseNodeDelete(name);
377      case code(':'):
378        return this.parseNodeInherit(name);
379      default:
380        this.dealWithError('syntax error, unknown node type');
381        break;
382    }
383
384    return null;
385  }
386
387  parseNodeInherit(name) {
388    if (
389      !this.lexer_.lex(this.current_) ||
390      (this.current_.type !== TokenType.LITERAL &&
391        this.current_.type !== TokenType.REF_PATH)
392    ) {
393      this.dealWithError('syntax error, miss node inherit path');
394      return null;
395    }
396
397    let inheritPath = this.current_.strval;
398
399    let node = this.parseNode(name);
400    if (node === null) {
401      return null;
402    }
403
404    node.setNodeType(NodeRefType.NODE_INHERIT);
405    node.setRefPath(inheritPath);
406    return node;
407  }
408
409  dealWithError(message) {
410    let errorLocation =
411      message +
412      ' at source ' +
413      this.current_.src +
414      ' at line ' +
415      this.current_.lineNo;
416    XMessage.gi().send('error', errorLocation);
417    NapiLog.logError(errorLocation + "'");
418  }
419}
420
421module.exports = {
422  Parser,
423};
424