• 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 { 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}