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 { TokenType } = require("./lexer") 18 19var ObjectType = { 20 PARSEROP_UINT8: 0x01, 21 PARSEROP_UINT16: 2, 22 PARSEROP_UINT32: 3, 23 PARSEROP_UINT64: 4, 24 PARSEROP_STRING: 5, 25 PARSEROP_CONFNODE: 6, 26 PARSEROP_CONFTERM: 7, 27 PARSEROP_ARRAY: 8, 28 PARSEROP_NODEREF: 9, 29 PARSEROP_DELETE: 10, 30 PARSEROP_BOOL: 11, 31}; 32 33var NodeRefType = { 34 NODE_NOREF: 0, 35 NODE_COPY: 1, 36 NODE_REF: 2, 37 NODE_DELETE: 3, 38 NODE_TEMPLATE: 4, 39 NODE_INHERIT: 5, 40}; 41 42var HcsErrorNo = { 43 NOERR: 0, /* No error */ 44 EFAIL: 1, /* Process fail */ 45 EOOM: 2, /* Out of memory */ 46 EOPTION: 3, /* Option error */ 47 EREOPENF: 4, /* Reopen argument */ 48 EINVALF: 5, /* Invalid file */ 49 EINVALARG: 6, /* Invalid argument */ 50 EDECOMP: 7, /* Decompile error */ 51 EOUTPUT: 8, /* Output error */ 52 EASTWALKBREAK: 9, /* Break ast walk */ 53}; 54 55var UINT8_MAX = 255; 56var UINT16_MAX = 65535; 57var UINT32_MAX = 0xffffffff; /* 4294967295U */ 58var UINT64_MAX = 0xffffffffffffffff; /* 18446744073709551615ULL */ 59 60class AstObject { 61 constructor(name, type, value, bindToken, jinzhi) { 62 let callType = Object.prototype.toString.call(name); 63 if (callType == "[object Object]") { 64 this.constructorSis(name.name_, name.type_, name.stringValue_); 65 this.integerValue_ = name.integerValue_; 66 } else if (callType == "[object String]") { 67 if (Object.prototype.toString.call(value) == "[object Number]") { 68 if (Object.prototype.toString.call(bindToken) == "[object Object]") { 69 this.constructorSiit(name, type, value, bindToken, jinzhi) 70 } 71 else { 72 this.constructorSii(name, type, value, jinzhi) 73 } 74 } 75 else if (Object.prototype.toString.call(value) == "[object String]") { 76 if (Object.prototype.toString.call(bindToken) == "[object Object]") { 77 this.constructorSist(name, type, value, bindToken) 78 } 79 else { 80 this.constructorSis(name, type, value) 81 } 82 } 83 else { 84 NapiLog.logError("err1"); 85 } 86 } else { 87 NapiLog.logError("err2") 88 } 89 } 90 91 constructorSii(name, type, value, jinzhi) { 92 this.type_ = type 93 this.name_ = name 94 this.parent_ = null; 95 this.lineno_ = 0 96 this.opCode_ = 0 97 this.size_ = 0 98 this.subSize_ = 0 99 this.hash_ = 0 100 this.integerValue_ = value 101 this.jinzhi_ = jinzhi 102 } 103 104 constructorSis(name, type, value) { 105 this.constructorSii(name, type, 0, 10) 106 this.stringValue_ = value 107 } 108 109 constructorSiit(name, type, value, bindToken, jinzhi) { 110 this.constructorSii(name, type, value, jinzhi) 111 this.lineno_ = bindToken.lineNo; 112 this.src_ = bindToken.src; 113 114 switch (type) { 115 case ObjectType.PARSEROP_UINT8: /* fall-through */ 116 case ObjectType.PARSEROP_UINT16: /* fall-through */ 117 case ObjectType.PARSEROP_UINT32: /* fall-through */ 118 case ObjectType.PARSEROP_UINT64: 119 this.type_ = this.fitIntegerValueType(value); 120 break; 121 default: 122 break; 123 } 124 } 125 126 constructorSist(name, type, value, bindToken) { 127 this.constructorSiit(name, type, 0, bindToken, 10); 128 this.stringValue_ = value; 129 } 130 131 fitIntegerValueType(value) { 132 if (value <= UINT8_MAX) { 133 return ObjectType.PARSEROP_UINT8; 134 } else if (value <= UINT16_MAX) { 135 return ObjectType.PARSEROP_UINT16; 136 } else if (value <= UINT32_MAX) { 137 return ObjectType.PARSEROP_UINT32; 138 } else { 139 return ObjectType.PARSEROP_UINT64; 140 } 141 } 142 addChild(childObj) { 143 if (childObj == null) { 144 return false; 145 } 146 if (this.child_ == null) { 147 this.child_ = childObj; 148 let childNext = childObj; 149 while (childNext != null) { 150 childNext.parent_ = this; 151 childNext = childNext.next_; 152 } 153 } else { 154 return this.child_.addPeer(childObj); 155 } 156 157 return true; 158 } 159 160 addPeer(peerObject) { 161 if (peerObject == null) { 162 return false; 163 } 164 165 if (this == peerObject) { 166 NapiLog.logError("add self as peer"); 167 return false; 168 } 169 170 if (this.next_ == null) { 171 this.next_ = peerObject; 172 } else { 173 let lastNode = this.next_; 174 while (lastNode.next_ != null) { 175 lastNode = lastNode.next_; 176 } 177 lastNode.next_ = peerObject; 178 } 179 180 let peer = peerObject; 181 while (peer) { 182 peer.parent_ = this.parent_; 183 peer = peer.next_; 184 } 185 186 return true; 187 } 188 189 // operate<< 190 merge(srcObj) { 191 if (srcObj.name_ != this.name_) { 192 NapiLog.logError(this.sourceInfo() + "merge different node to" + srcObj.sourceInfo()); 193 return false; 194 } 195 196 if (srcObj.type_ != this.type_) { 197 NapiLog.logError(this.sourceInfo() + "conflict type with " + srcObj.sourceInfo()); 198 return false; 199 } 200 201 this.src_ = srcObj.src_; 202 this.lineno_ = srcObj.lineno_; 203 this.stringValue_ = srcObj.stringValue_; 204 this.integerValue_ = srcObj.integerValue_; 205 206 return true; 207 } 208 copy(src, overwrite) { 209 if (src == null) { 210 return false; 211 } 212 213 if (overwrite) { 214 this.src_ = src.src_; 215 this.lineno_ = src.lineno_; 216 this.integerValue_ = src.integerValue_; 217 this.stringValue_ = src.stringValue_; 218 } 219 220 return true; 221 } 222 223 move(src) { 224 if (!this.copy(src, true)) { 225 return false; 226 } 227 src.separate(); 228 return true; 229 } 230 231 sourceInfo() { 232 return this.src_ + ":" + this.lineno_ + " "; 233 } 234 235 remove() { 236 this.separate(); 237 this.child_ = null; 238 this.next_ = null; 239 } 240 241 lookup(name, type) { 242 let peer = this.child_; 243 while (peer != null) { 244 if (peer.name_ == name && (type == 0 || peer.type_ == type)) { 245 return peer; 246 } 247 248 peer = peer.next_; 249 } 250 251 return null; 252 } 253 254 isNumber() { 255 return this.type_ >= ObjectType.PARSEROP_UINT8 && type_ <= ObjectType.PARSEROP_UINT64; 256 } 257 258 isNode() { 259 return this.type_ == ObjectType.PARSEROP_CONFNODE; 260 } 261 262 isTerm() { 263 return this.type_ == ObjectType.PARSEROP_CONFTERM; 264 } 265 isArray() { 266 return this.type_ == ObjectType.PARSEROP_ARRAY; 267 } 268 separate() { 269 if (this.parent_ == null) { 270 return; 271 } 272 if (this.parent_.child_ == this) { 273 this.parent_.child_ = this.next_; 274 this.next_ = null; 275 return; 276 } 277 278 let pre = this.parent_.child_; 279 while (pre != null) { 280 if (pre.next_ == this) { 281 let tmp = this.next_; 282 this.next_ = null; 283 pre.next_ = tmp; 284 break; 285 } 286 287 pre = pre.next_; 288 } 289 } 290 setParent(parent) { 291 this.parent_ = parent; 292 } 293 SetSize(size) { 294 this.size_ = size; 295 } 296 SetSubSize(size) { 297 this.subSize_ = size; 298 } 299 GetSubSize() { 300 return this.subSize_; 301 } 302 SetHash(hash) { 303 this.hash_ = hash; 304 } 305 GetSize() { 306 return this.size_; 307 } 308 GetHash() { 309 return this.hash_; 310 } 311 next() { 312 return this.next_; 313 } 314 child() { 315 return this.child_; 316 } 317 name() { 318 return this.name_; 319 } 320 stringValue() { 321 return this.stringValue_; 322 } 323 IntegerValue() { 324 return this.integerValue_; 325 } 326 type() { 327 return this.type_; 328 } 329 OpCode() { 330 return this.opCode_; 331 } 332 SetOpCode(opcode) { 333 this.opCode_ = opcode; 334 } 335 hasDuplicateChild() { 336 return false; 337 } 338 isElders(child) { 339 let p = child; 340 while (p != null) { 341 if (p == this) { 342 return true; 343 } 344 p = p.parent_; 345 } 346 return false; 347 } 348 parent() { 349 return this.parent_; 350 } 351} 352 353class ConfigNode extends AstObject { 354 constructor(name, nodeType, refName) { 355 if (Object.prototype.toString.call(name) == "[object String]") { 356 super(name, ObjectType.PARSEROP_CONFNODE, ""); 357 this.refNodePath_ = refName; 358 this.nodeType_ = nodeType; 359 this.inheritIndex_ = 0; 360 this.inheritCount_ = 0; 361 this.templateSignNum_ = 0; 362 } 363 else if (Object.prototype.toString.call(nodeType) == "[object Number]") { 364 super(name.strval, ObjectType.PARSEROP_CONFNODE, 0, name); 365 this.refNodePath_ = refName; 366 this.nodeType_ = nodeType; 367 this.inheritIndex_ = 0; 368 this.inheritCount_ = 0; 369 this.templateSignNum_ = 0; 370 } 371 else { 372 super(name, ObjectType.PARSEROP_CONFNODE, ""); 373 this.refNodePath_ = refName; 374 this.nodeType_ = nodeType; 375 this.inheritIndex_ = 0; 376 this.inheritCount_ = 0; 377 this.templateSignNum_ = 0; 378 379 let child = name.child_; 380 while (child != null) { 381 super.addChild(AstObjectFactory.build(child)); 382 child = child.next(); 383 } 384 } 385 this.subClasses_ = []; 386 } 387 NodeTypeToStr(type) { 388 let type2StringMap = { 389 0: "", 390 1: "NodeCopy", 391 2: "NodeReference", 392 3: "NodeDelete", 393 4: "NodeInherit", 394 5: "NodeTemplate", 395 }; 396 return type2StringMap[type]; 397 } 398 399 // operator<< 400 castFrom(astObject) { 401 return astObject; 402 } 403 getNodeType() { 404 return this.nodeType_; 405 } 406 getRefPath() { 407 return this.refNodePath_; 408 } 409 merge(srcObj) { 410 if (srcObj == null) { 411 return true; 412 } 413 if (!srcObj.isNode() || srcObj.name() != this.name_) { 414 NapiLog.logError( sourceInfo() + "merge conflict type with " + srcObj.sourceInfo()); 415 return false; 416 } 417 418 let srcNode = srcObj; 419 if (srcNode.getNodeType() == TokenType.DELETE) { 420 srcObj.separate(); 421 this.separate(); 422 return true; 423 } 424 425 this.nodeType_ = srcNode.nodeType_; 426 this.refNodePath_ = srcNode.refNodePath_; 427 428 let childSrc = srcObj.child(); 429 while (childSrc != null) { 430 let childSrcNext = childSrc.next(); 431 let childDst = this.lookup(childSrc.name(), childSrc.type()); 432 if (childDst == null) { 433 childSrc.separate(); 434 this.addChild(childSrc); 435 } else if (!childDst.merge(childSrc)){ 436 return false; 437 } 438 childSrc = childSrcNext; 439 } 440 return true; 441 } 442 setNodeType(nodeType) { 443 this.nodeType_ = nodeType; 444 } 445 setRefPath(ref) { 446 this.refNodePath_ = ref; 447 } 448 hasDuplicateChild() { 449 let symMap = {}; 450 let child = this.child_; 451 while (child != null) { 452 let sym = symMap[child.name()]; 453 if (sym != undefined) { 454 NapiLog.logError(child.sourceInfo() + "redefined, first definition at " + sym.second.sourceInfo()); 455 return true; 456 } 457 symMap[child.name()] = child; 458 child = child.next(); 459 } 460 461 return false; 462 } 463 inheritExpand(refObj) { 464 if (refObj == null) { 465 NapiLog.logError(sourceInfo() + "inherit invalid node: " + refNodePath_); 466 return false; 467 } 468 469 if (!this.copy(refObj, false)) { 470 return false; 471 } 472 473 let templateNode = this.castFrom(refObj); 474 if (!this.compare(templateNode)) { 475 return false; 476 } 477 this.inheritIndex_ = templateNode.inheritCount_++; 478 templateNode.subClasses_.push(this); 479 return true; 480 } 481 refExpand(refObj) { 482 if (this.nodeType_ == NodeRefType.NODE_DELETE) { 483 this.separate(); 484 return true; 485 } 486 487 if (refObj.isElders(this)) { 488 NapiLog.logError(sourceInfo() + "circular reference " + refObj.sourceInfo()); 489 return false; 490 } 491 492 let ret = true; 493 if (this.nodeType_ == NodeRefType.NODE_REF) { 494 ret = this.nodeRefExpand(refObj); 495 } else if (nodeType_ == NodeRefType.NODE_COPY) { 496 ret = nodeCopyExpand(refObj); 497 } 498 499 return ret; 500 } 501 copy(src, overwrite) { 502 let child = src.child(); 503 while (child != null) { 504 let dst = this.lookup(child.name(), child.type()); 505 if (dst == null) { 506 this.addChild(AstObjectFactory.build(child)); 507 } else if (!dst.copy(child, overwrite)){ 508 return false; 509 } 510 child = child.next(); 511 } 512 513 return true; 514 } 515 move(src) { 516 return super.move(src); 517 } 518 nodeRefExpand(ref) { 519 if (ref == null) { 520 NapiLog.logError(sourceInfo() + "reference node '" + refNodePath_ + "' not exist"); 521 return false; 522 } 523 return ref.move(this); 524 } 525 nodeCopyExpand(ref) { 526 if (ref == null) { 527 NapiLog.logError(sourceInfo() + "copy node '" + refNodePath_ + "' not exist"); 528 return false; 529 } 530 this.nodeType_ = NodeRefType.NODE_NOREF; 531 return this.copy(ref, false); 532 } 533 compare(other) { 534 let objChild = this.child_; 535 while (objChild != null) { 536 let baseObj = this.lookup(objChild.name(), objChild.type()); 537 if (baseObj == null) { 538 NapiLog.logError(objChild.sourceInfo() + "not in template node: " + other.sourceInfo()); 539 return false; 540 } 541 if (objChild.isNode()) { 542 return this.castFrom(objChild).compare(this.castFrom(baseObj)); 543 } 544 545 objChild = objChild.next(); 546 } 547 return true; 548 } 549 InheritIndex() { 550 return this.inheritIndex_; 551 } 552 InheritCount() { 553 return this.inheritCount_; 554 } 555 TemplateSignNum() { 556 return this.templateSignNum_; 557 } 558 SetTemplateSignNum(sigNum) { 559 this.templateSignNum_ = sigNum; 560 } 561 SubClasses() { 562 return this.subClasses_; 563 } 564} 565 566class ConfigTerm extends AstObject { 567 constructor(name, value) { 568 if (Object.prototype.toString.call(name) == "[object String]") { 569 super(name, ObjectType.PARSEROP_CONFTERM, 0); 570 this.signNum_ = 0; 571 this.child_ = value; 572 if (value != null) value.this.setParent(this); 573 } 574 else if (Object.prototype.toString.call(value) == "[object Object]" || 575 Object.prototype.toString.call(value) == "[object Null]") { 576 super(name.strval, ObjectType.PARSEROP_CONFTERM, 0, name); 577 this.signNum_ = 0; 578 this.child_ = value; 579 if (value != null) value.this.setParent(this); 580 } 581 else { 582 super(name.name_, ObjectType.PARSEROP_CONFTERM, 0); 583 this.signNum_ = 0; 584 this.child_ = value; 585 if (value != null) value.this.setParent(this); 586 super.addChild(AstObjectFactory.build(name.child_)); 587 } 588 } 589 590 // operator<< 591 castFrom(astObject) { 592 return astObject; 593 } 594 merge(srcObj) { 595 if (!srcObj.isTerm()) { 596 NapiLog.logError(sourceInfo() + "merge conflict type with " + srcObj.sourceInfo()); 597 return false; 598 } 599 600 let value = srcObj.child(); 601 srcObj.child().separate(); 602 this.child_ = null; 603 this.addChild(value); 604 return true; 605 } 606 607 refExpand(refObj) { 608 if (child_.type() == ObjectType.PARSEROP_DELETE) { 609 this.separate(); 610 return true; 611 } 612 613 if (child_.type() != ObjectType.PARSEROP_NODEREF) { 614 return true; 615 } 616 617 if (refObj == null || !refObj.isNode() || 618 ConfigNode.castFrom(refObj).getNodeType() == NodeRefType.NODE_REF || 619 ConfigNode.castFrom(refObj).getNodeType() == NodeRefType.NODE_TEMPLATE || 620 ConfigNode.castFrom(refObj).getNodeType() == NodeRefType.NODE_DELETE) { 621 NapiLog.logError(sourceInfo() + "reference invalid node '" + child_.stringValue() + '\''); 622 return false; 623 } 624 625 this.refNode_ = refObj; 626 return true; 627 } 628 copy(src, overwrite) { 629 if (!overwrite) { 630 return true; 631 } 632 if (this.child_.type() != src.child().type() && (!this.child_.isNumber() || !src.child().isNumber())) { 633 NapiLog.logError(src.sourceInfo() + "overwrite different type with:" + child_.sourceInfo()); 634 return false; 635 } 636 return this.child_.copy(src.child(), overwrite); 637 } 638 move(src) { 639 return this.child_.move(src.child()); 640 } 641 RefNode() { 642 return this.refNode_; 643 } 644 SetSigNum(sigNum) { 645 this.signNum_ = sigNum; 646 } 647 SigNum() { 648 return this.signNum_; 649 } 650} 651class ConfigArray extends AstObject { 652 constructor(array) { 653 if (Object.prototype.toString.call(array) == "[object Object]") { 654 super("", ObjectType.PARSEROP_ARRAY, 0, array); // bindToken 655 this.arrayType_ = 0; 656 this.arraySize_ = 0; 657 if (array.type == undefined) { 658 let child = array.child_; 659 while (child != null) { 660 super.addChild(AstObjectFactory.build(child)); 661 child = child.next(); 662 } 663 this.arraySize_ = array.arraySize_; 664 this.arrayType_ = array.arrayType_; 665 } 666 } 667 else { 668 super("", ObjectType.PARSEROP_ARRAY, 0); 669 this.arrayType_ = 0; 670 this.arraySize_ = 0; 671 } 672 673 } 674 675 addChild(childObj) { 676 if (super.addChild(childObj)) { 677 this.arraySize_++; 678 this.arrayType_ = this.arrayType_ > childObj.type() ? this.arrayType_ : childObj.type(); 679 return true; 680 } else { 681 return false; 682 } 683 } 684 685 merge(srcObj) { 686 if (!srcObj.isArray()) { 687 NapiLog.logError(sourceInfo() + "merge conflict type with " + srcObj.sourceInfo()); 688 return false; 689 } 690 691 let value = srcObj.child(); 692 value.separate(); 693 this.child_ = value; 694 return true; 695 } 696 697 copy(src, overwrite) { 698 if (!overwrite) { 699 return true; 700 } 701 let array = ConfigArray.castFrom(src); 702 this.child_ = null; 703 let t = array.child_; 704 while (t != null) { 705 addChild(AstObjectFactory.build(t)); 706 } 707 return true; 708 } 709 710 castFrom(astObject) { 711 return astObject; 712 } 713 714 arraySize() { 715 return this.arraySize_; 716 } 717 718 arrayType() { 719 return this.arrayType_; 720 } 721} 722 723class AstObjectFactory { } 724AstObjectFactory.build = function (object) { 725 switch (object.type()) { 726 case ObjectType.PARSEROP_CONFNODE: 727 return new ConfigNode(object); 728 case ObjectType.PARSEROP_CONFTERM: 729 return new ConfigTerm(object); 730 case ObjectType.PARSEROP_ARRAY: 731 return new ConfigArray(object); 732 default: 733 return new AstObject(object); 734 } 735} 736 737class Ast { 738 constructor(astRoot) { 739 this.astRoot_ = astRoot; 740 this.redefineChecked_ = false; 741 } 742 setw(l) { 743 let ret = ""; 744 for (let i = 0; i < l; i++)ret += " "; 745 return ret; 746 } 747 748 dump(prefix) { 749 NapiLog.logError("dump ", prefix, " AST:"); 750 this.walkForward(this.astRoot_, (current, walkDepth) => { 751 switch (current.type_) { 752 case ObjectType.PARSEROP_UINT8: 753 case ObjectType.PARSEROP_UINT16: 754 case ObjectType.PARSEROP_UINT32: 755 case ObjectType.PARSEROP_UINT64: 756 NapiLog.logInfo(this.setw(walkDepth * 4) + "[" + current.integerValue_ + "]"); break; 757 case ObjectType.PARSEROP_STRING:// 5 758 NapiLog.logInfo(this.setw(walkDepth * 4) + current.stringValue_); break; 759 case ObjectType.PARSEROP_CONFNODE:// 6 content 760 if (current.name_ == "blockSize") { 761 current.name_ = "blockSize" 762 } 763 NapiLog.logInfo(this.setw(walkDepth * 4) + current.name_ + " :"); break; 764 case ObjectType.PARSEROP_CONFTERM:// 7 Attribute name 765 if (current.name_ == "funcNum") { 766 current.name_ = "funcNum" 767 } 768 NapiLog.logInfo(this.setw(walkDepth * 4) + current.name_ + " = "); break; 769 case ObjectType.PARSEROP_ARRAY: 770 NapiLog.logInfo(this.setw(walkDepth * 4) + current.name_); break; 771 case ObjectType.PARSEROP_NODEREF: 772 NapiLog.logInfo(this.setw(walkDepth * 4) + current.name_); break; 773 case ObjectType.PARSEROP_DELETE: 774 NapiLog.logInfo(this.setw(walkDepth * 4) + current.name_); break; 775 } 776 return HcsErrorNo.NOERR; 777 }); 778 } 779 780 getAstRoot() { 781 return this.astRoot_; 782 } 783 784 merge(astList) { 785 if (!this.redefineCheck()) { 786 return false; 787 } 788 for (let i = 0; i < astList.length; i++) { 789 let astIt = astList[i]; 790 if (!astIt.redefineCheck()) { 791 return false; 792 } 793 if (this.astRoot_ != null && !this.astRoot_.merge(astIt.astRoot_)) { 794 return false; 795 } else if (this.astRoot_ == null) { 796 this.astRoot_ = astIt.getAstRoot(); 797 } 798 } 799 return true; 800 } 801 802 getchild(node, name) { 803 let p = node.child_ 804 while (p != null) { 805 if (p.name_ == name) return p; 806 p = p.next_; 807 } 808 return null; 809 } 810 811 getpath(node) { 812 NapiLog.logError("----path start----") 813 let p = node 814 while (p != null) { 815 NapiLog.logError(p.name_) 816 p = p.parent_ 817 } 818 NapiLog.logError("----path end----") 819 } 820 821 expand() { 822 let n1, n2; 823 824 if (!this.redefineCheck()) { 825 return false; 826 } 827 828 if (this.astRoot_.lookup("module", ObjectType.PARSEROP_CONFTERM) == null) { 829 NapiLog.logError(astRoot_.sourceInfo() + "miss 'module' attribute under root node"); 830 return false; 831 } 832 833 if (!this.nodeExpand()) { 834 return false; 835 } 836 837 if (!this.inheritExpand()) { 838 return false; 839 } 840 this.dump("expanded"); 841 return true; 842 } 843 844 nodeExpand() { 845 return this.walkBackward(this.astRoot_, (current, walkDepth) => { 846 if (current.isNode()) { 847 let node = current; 848 if (node.getNodeType() == NodeRefType.NODE_DELETE) { 849 current.remove();child_ 850 return HcsErrorNo.NOERR; 851 } 852 if (node.getNodeType() != NodeRefType.NODE_REF && node.getNodeType() != NodeRefType.NODE_COPY) { 853 return HcsErrorNo.NOERR; 854 } 855 856 // current node maybe deleted after reference expand, never use it after this 857 let ret = node.refExpand(this.lookup(current, node.getRefPath())); 858 if (!ret) { 859 return HcsErrorNo.EFAIL; 860 } 861 } else if (current.isTerm()) { 862 let ref; 863 if (current.child_.type() == ObjectType.PARSEROP_DELETE) { 864 current.remove(); 865 return HcsErrorNo.NOERR; 866 } 867 if (current.child_.type() == ObjectType.PARSEROP_NODEREF) { 868 ref = lookup(current, current.child_.stringValue()); 869 if (!current.refExpand(ref)) { 870 return HcsErrorNo.EFAIL; 871 } 872 } 873 } 874 return HcsErrorNo.NOERR; 875 }); 876 } 877 878 walkBackward(startObject, callback) { 879 let backWalkObj = startObject; 880 let next = null; 881 let parent = null; 882 let walkDepth = 0; 883 let preVisited = false; 884 885 while (backWalkObj != null) { 886 let backWalk = true; 887 if (backWalkObj.child_ == null || preVisited) { 888 next = backWalkObj.next_; 889 parent = backWalkObj.parent(); 890 891 /* can safe delete current in callback */ 892 if (callback(backWalkObj, walkDepth) != HcsErrorNo.NOERR) { 893 return false; 894 } 895 } else if (backWalkObj.child_) { 896 walkDepth++; 897 backWalkObj = backWalkObj.child_; 898 backWalk = false; 899 } 900 if (backWalk) { 901 if (backWalkObj == startObject) { 902 break; 903 } 904 905 if (next != null) { 906 backWalkObj = next; 907 preVisited = false; 908 } else { 909 backWalkObj = parent; 910 preVisited = true; 911 walkDepth--; 912 } 913 } 914 } 915 return true; 916 } 917 918 inheritExpand() { 919 return this.walkForward(this.astRoot_, (current, ii) => { 920 if (current.isNode()) { 921 let node = current; 922 if (node.getNodeType() != NodeRefType.NODE_INHERIT) { 923 return HcsErrorNo.NOERR; 924 } 925 let inherit = this.lookup(current, node.getRefPath()); 926 if (!node.inheritExpand(inherit)) { 927 return HcsErrorNo.EFAIL; 928 } 929 } 930 931 return HcsErrorNo.NOERR; 932 }); 933 } 934 935 redefineCheck() { 936 if (this.redefineChecked_) { 937 return true; 938 } 939 940 let ret = this.walkForward(this.astRoot_, (current, ii) => { 941 if (current.isNode() && current.hasDuplicateChild()) { 942 return HcsErrorNo.EFAIL; 943 } 944 945 return HcsErrorNo.NOERR; 946 }); 947 948 this.redefineChecked_ = true; 949 return ret; 950 } 951 952 walkForward(startObject, callback) { 953 let forwardWalkObj = startObject; 954 let walkDepth = 0; 955 let preVisited = false; 956 957 while (forwardWalkObj != null) { 958 let forward = true; 959 if (!preVisited) { 960 let ret = callback(forwardWalkObj, walkDepth); 961 if (ret && ret != HcsErrorNo.EASTWALKBREAK) { 962 return false; 963 } else if (ret != HcsErrorNo.EASTWALKBREAK && forwardWalkObj.child_ != null) { 964 965 /* when callback return HcsErrorNo.EASTWALKBREAK, not walk current's child */ 966 walkDepth++; 967 forwardWalkObj = forwardWalkObj.child_; 968 forward = false; 969 } 970 } 971 972 if (forward) { 973 if (forwardWalkObj == startObject) { 974 break; 975 } 976 977 if (forwardWalkObj.next_ != null) { 978 forwardWalkObj = forwardWalkObj.next_; 979 preVisited = false; 980 } else { 981 forwardWalkObj = forwardWalkObj.parent(); 982 preVisited = true; 983 walkDepth--; 984 } 985 } 986 987 } 988 989 return true; 990 } 991 992 lookup(startObj, path) { 993 if (path.indexOf('.') < 0) { 994 return startObj.parent_.lookup(path, 0); 995 } 996 997 let splitPath = this.splitNodePath(path, '.'); 998 } 999 splitNodePath(path, separator) { 1000 let splitList = path.split(separator) 1001 return splitList; 1002 } 1003} 1004 1005module.exports = { 1006 AstObject, 1007 ConfigNode, 1008 ConfigTerm, 1009 ConfigArray, 1010 NodeRefType, 1011 ObjectType, 1012 Ast 1013}