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}