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