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