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