package stdx.syntax

import std.collection.ArrayList

public open class ASTRewriter {
    public func walk(startPoint: SyntaxTreeNode, detach!: Bool = false): SyntaxTreeNode {
        let newNode = match (startPoint) {
            case pkg: Package => walkAndRewritePackage(pkg)
            case _ => walkAndRewrite(startPoint)
        }
        // If the node has not been rewritten, there is no need to refresh the node's parent node.
        // The package node has no parent node and does not need to be refreshed.
        if ((!detach && newNode == startPoint) || startPoint is Package) {
            return newNode
        }
        // refresh parent node
        let newParent: ?SyntaxTreeNode = match (detach) {
            case false => getNewParent(startPoint.parentNode, newNode.nodeImpl, startPoint.nodePos.begin)
            case true => None
        }
        return SyntaxNodeImplTranslator.translate(newNode.nodeImpl, startPoint.nodePos.begin, newParent).getOrThrow()
    }

    private func isRewritedChild(hasRewritedChild: Bool, new: SyntaxNodeImpl, old: SyntaxNodeImpl) {
        if (new.id != old.id) {
            return true
        }
        return hasRewritedChild
    }

    private func walkAndRewritePackage(node: Package): Package {
        var hasRewritedChild = false
        let fileList = ArrayList<SourceFile>()
        let fileImpls = ArrayList<(SyntaxNodeImpl, String)>()
        for (file in node.srcFile) {
            let newNode = (walkAndRewrite<SourceFile>(file) as SourceFile).getOrThrow()
            hasRewritedChild = isRewritedChild(hasRewritedChild, newNode.nodeImpl, file.nodeImpl)
            fileImpls.add((newNode.nodeImpl, newNode.path))
            fileList.add(newNode)
        }

        let rewritedNode = if (hasRewritedChild) {
            let (isMacro, name) = checkPackage(fileList)
            let pkg = Package(isMacro, name, fileImpls.toArray())
            rewrite(pkg)
        } else {
            rewrite(node)
        }
        checkNodeKind(rewritedNode, node)
        (rewritedNode as Package).getOrThrow()
    }

    private func walkAndRewrite<T>(node: T): SyntaxTreeNode where T <: SyntaxTreeNode {
        var hasRewritedChild = false
        let childCollector = ChildCollector()
        childCollector.walk(node)
        let collectChildNodeList = childCollector.getChildNodes()
        if (collectChildNodeList.size == 0) {
            return cast<T>(rewrite(node)).getOrThrow()
        }
        let comments = ArrayList<Comment>(node.comments)
        let newNodeImpl = ArrayList<SyntaxNodeImpl>()
        match (node.nodeImpl) {
            case nt: NonTerminal => for (child in nt.children) {
                if (child is Terminal) {
                    newNodeImpl.add(child)
                    continue
                }
                // intermediate node in SyntaxNodeImpl
                if (child.kind == SyntaxNodeKind.CommentGroupList || child.kind == SyntaxNodeKind.AnnotationList ||
                    child.kind == SyntaxNodeKind.ModifierList || child.kind == SyntaxNodeKind.TypeArguments ||
                    child.kind == SyntaxNodeKind.MatchCaseBody || child.kind == SyntaxNodeKind.TokenList) {
                    hasRewritedChild = walkAndRewriteSpecialNode(child, newNodeImpl, collectChildNodeList, comments)
                    continue
                }
                let n = collectChildNodeList.remove(at: 0)
                let newNode = walkAndRewrite(n)
                hasRewritedChild = isRewritedChild(hasRewritedChild, newNode.nodeImpl, child)
                newNodeImpl.add(newNode.nodeImpl)
                // VArrayType only has one child: Property 'size' is NonTerminal, not SyntaxTreeNode
                if (node.nodeImpl.kind == SyntaxNodeKind.VArrayType) {
                    break
                }
            }
            case _ => throw Exception("RewriteException: SyntaxNodeImpl kind not match.")
        }
        let rewritedNode = if (hasRewritedChild) {
            let nodeImpl = NonTerminal(node.nodeImpl.kind, newNodeImpl.toArray())
            let newNode = SyntaxNodeImplTranslator.translate(nodeImpl, node.nodePos.begin, node.parentNode)
            rewrite(newNode.getOrThrow())
        } else {
            rewrite(node)
        }
        checkNodeKind(rewritedNode, node)
        return rewritedNode
    }

    // Some SyntaxNodeImpl nodes do not match SyntaxTreeNode nodes. 
    // This function is used to handle special cases with intermediate layers.
    private func walkAndRewriteSpecialNode(impl: SyntaxNodeImpl, newNodeImpl: ArrayList<SyntaxNodeImpl>,
        collectChildNodeList: ArrayList<SyntaxTreeNode>, comments: ArrayList<Comment>) {
        var hasRewritedChild = false
        match (impl.kind) {
            case SyntaxNodeKind.CommentGroupList =>
                var newCommentGroupListImpl = NonTerminal(impl.kind, Array<SyntaxNodeImpl>())
                (hasRewritedChild, newCommentGroupListImpl) = walkAndRewriteComment(impl, comments)
                newNodeImpl.add(newCommentGroupListImpl)
            case SyntaxNodeKind.AnnotationList | SyntaxNodeKind.ModifierList | SyntaxNodeKind.TypeArguments
                | SyntaxNodeKind.MatchCaseBody =>
                var newListImpl = NonTerminal(impl.kind, Array<SyntaxNodeImpl>())
                (hasRewritedChild, newListImpl) = walkAndRewriteNonTerminalList(impl, collectChildNodeList)
                newNodeImpl.add(newListImpl)
            case SyntaxNodeKind.TokenList => newNodeImpl.add(impl)
            case _ => ()
        }
        return hasRewritedChild
    }

    private func walkAndRewriteNonTerminalList(listImpl: SyntaxNodeImpl, childNodeList: ArrayList<SyntaxTreeNode>) {
        var hasRewritedChild = false
        let list = (listImpl as NonTerminal).getOrThrow()
        let newNodeImpl = ArrayList<SyntaxNodeImpl>()
        for (child in list.children) {
            // foreign is NonTerminal in ModifierList, but is not SyntaxTreeNode(Modifier)
            if (child is NonTerminal && child.toString() != "foreign") {
                let n = childNodeList.remove(at: 0)
                let newNode = walkAndRewrite(n)
                hasRewritedChild = isRewritedChild(hasRewritedChild, newNode.nodeImpl, child)
                newNodeImpl.add(newNode.nodeImpl)
            } else {
                newNodeImpl.add(child)
            }
        }
        return if (hasRewritedChild) {
            (hasRewritedChild, NonTerminal(listImpl.kind, newNodeImpl.toArray()))
        } else {
            (hasRewritedChild, list)
        }
    }

    func getNewCommentGroup(nt: NonTerminal, comments: ArrayList<Comment>): (Array<SyntaxNodeImpl>, Bool) {
        var hasRewritedChild = false
        var newCommentGroup = ArrayList<SyntaxNodeImpl>()
        for (comment in nt.children) {
            if (comment is NonTerminal) {
                let n = comments.remove(at: 0)
                let newNode = walkAndRewrite(n)
                hasRewritedChild = isRewritedChild(hasRewritedChild, newNode.nodeImpl, comment)
                newCommentGroup.add(newNode.nodeImpl)
            } else {
                newCommentGroup.add(comment)
            }
        }
        return (newCommentGroup.toArray(), hasRewritedChild)
    }

    private func walkAndRewriteComment(groupListImpl: SyntaxNodeImpl, comments: ArrayList<Comment>) {
        var hasRewritedChild = false
        var newCommentGroup = Array<SyntaxNodeImpl>()
        let list = (groupListImpl as NonTerminal).getOrThrow()
        let newNodeImpl = ArrayList<SyntaxNodeImpl>()
        for (child in list.children) {
            // child is CommentGruop or NL
            match (child) {
                case nt: NonTerminal =>
                    (newCommentGroup, hasRewritedChild) = getNewCommentGroup(nt, comments)
                    newNodeImpl.add(NonTerminal(SyntaxNodeKind.CommentGroup, newCommentGroup))
                case _ => newNodeImpl.add(child)
            }
        }
        return if (hasRewritedChild) {
            (hasRewritedChild, NonTerminal(SyntaxNodeKind.CommentGroupList, newNodeImpl.toArray()))
        } else {
            (hasRewritedChild, list)
        }
    }

    private func checkNodeKind(newNode: SyntaxTreeNode, oldNode: SyntaxTreeNode) {
        if (newNode.nodeImpl.kind != oldNode.nodeImpl.kind) {
            throw Exception(
                "RewriteException: The type of the rewritten node does not match the type of the original node.")
        }
    }

    public open func rewrite(node: SyntaxTreeNode): SyntaxTreeNode {
        return node
    }
}

func getNewPackage(oldPackage: Package, fileImpl: SyntaxNodeImpl, filePath: String) {
    // need to replace this fileImpl node in oldPackage
    let newFile = (SyntaxNodeImplTranslator
        .translateFile(fileImpl, filePath, parent: oldPackage)
        .getOrThrow() as SourceFile)
        .getOrThrow()
    let fileList = ArrayList<SourceFile>()
    let fileImpls = ArrayList<(SyntaxNodeImpl, String)>()
    for (file in oldPackage.srcFile) {
        if (file.name == newFile.name) {
            fileImpls.add((newFile.nodeImpl, newFile.path))
            fileList.add(newFile)
        } else {
            fileImpls.add((file.nodeImpl, file.path))
            fileList.add(file)
        }
    }
    let (isMacro, name) = checkPackage(fileList)
    Package(isMacro, name, fileImpls.toArray())
}

// Refresh the parent node, replace the old child nodes in the parent node with new ones, and create a new parent node.
func getNewParent(oldParent: ?SyntaxTreeNode, impl: SyntaxNodeImpl, targetPos: CodePosition): ?SyntaxTreeNode {
    if (let Some(node) <- oldParent && let Some(pkg) <- (node as Package)) {
        return getNewPackage(pkg, impl, targetPos.filePath)
    }
    return if (let Some(node) <- oldParent) {
        // parentNode must be NonTerminal
        let oldParentImpl = ((node.nodeImpl) as NonTerminal).getOrThrow()
        var curPos = CodePosition(node.nodePos.beginLine, node.nodePos.beginColumn,
            FileInfos(node.nodePos.fileName, node.nodePos.filePath))
        let children = ArrayList<SyntaxNodeImpl>()
        for (child in oldParentImpl.children) {
            if (curPos == targetPos) {
                children.add(impl)
            } else {
                children.add(child)
            }
            curPos = curPos + child.offset
        }
        let newParentImpl = NonTerminal(oldParentImpl.kind, children.toArray())
        let newParent: ?SyntaxTreeNode = match (node.parentNode) {
            case Some(parent) => getNewParent(parent, newParentImpl, node.nodePos.begin)
            case None => node
        }
        SyntaxNodeImplTranslator.translate(newParentImpl, node.nodePos.begin, newParent)
    } else {
        None
    }
}

class ChildCollector <: ASTVisitor {
    private var childNodes: ArrayList<SyntaxTreeNode> = ArrayList<SyntaxTreeNode>()

    public override func preAction(node: SyntaxTreeNode): PreActionMode {
        clearChildNodes()
        collectChildNodes(node)
        return PreActionMode.Skip
    }

    private func collectChildNodes(node: SyntaxTreeNode): Unit {
        match (node) {
            case binaryExpr: BinaryExpr =>
                childNodes.add(binaryExpr.lhs)
                childNodes.add(binaryExpr.rhs)
            case varDecl: VarDecl =>
                for (a in varDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in varDecl.modifiers) {
                    childNodes.add(m)
                }
                childNodes.add(varDecl.pattern)
                if (let Some(tyAnnotation) <- varDecl.tyAnnotation) {
                    childNodes.add(tyAnnotation)
                }
                if (let Some(initializer) <- varDecl.initializer) {
                    childNodes.add(initializer)
                }
            case symbolRef: SymbolRef => for (t in symbolRef.typeArguments) {
                childNodes.add(t)
            }
            case annotation: Annotation => for (arg in annotation.arguments) {
                childNodes.add(arg)
            }
            case argument: Argument => childNodes.add(argument.value)
            case arrayLiteral: ArrayLiteral => for (element in arrayLiteral.elements) {
                childNodes.add(element)
            }
            case asExpr: AsExpr =>
                childNodes.add(asExpr.srcVal)
                childNodes.add(asExpr.targetTypeAnnotation)
            case assignExpr: AssignExpr =>
                childNodes.add(assignExpr.lhs)
                childNodes.add(assignExpr.rhs)
            case block: Block => for (n in block.nodes) {
                childNodes.add(n)
            }
            case body: Body => for (decl in body.memberDecls) {
                childNodes.add(decl)
            }
            case callExpr: CallExpr =>
                childNodes.add(callExpr.callee)
                for (arg in callExpr.arguments) {
                    childNodes.add(arg)
                }
            case catchPattern: CatchPattern =>
                childNodes.add(catchPattern.pattern)
                for (ty in catchPattern.exceptionType) {
                    childNodes.add(ty)
                }
            case classDecl: ClassDecl =>
                for (a in classDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in classDecl.modifiers) {
                    childNodes.add(m)
                }
                for (param in classDecl.genericParams) {
                    childNodes.add(param)
                }
                for (superTy in classDecl.superTyAnnotations) {
                    childNodes.add(superTy)
                }
                if (let Some(constraints) <- classDecl.genericConstraints) {
                    childNodes.add(constraints)
                }
                childNodes.add(classDecl.body)
            case compositeType: CompositeType => for (arg in compositeType.typeArguments) {
                childNodes.add(arg)
            }
            case conjunctionCondition: ConjunctionCondition => for (cond in conjunctionCondition.cond) {
                match (cond) {
                    case LetPatternCondition(letPattern) => childNodes.add(letPattern)
                    case Expression(expr) => childNodes.add(expr)
                    case ParenConditionConstructor(parenCondition) => childNodes.add(parenCondition)
                    case _ => throw Exception()
                }
            }
            case constPattern: ConstPattern => childNodes.add(constPattern.litConstExpr)
            case disjunctionCondition: DisjunctionCondition => for (cond in disjunctionCondition.cond) {
                childNodes.add(cond)
            }
            case doWhileExpr: DoWhileExpr =>
                childNodes.add(doWhileExpr.body)
                childNodes.add(doWhileExpr.condition)
            case enumConstructor: EnumConstructor =>
                for (a in enumConstructor.annotations) {
                    childNodes.add(a)
                }
                for (m in enumConstructor.modifiers) {
                    childNodes.add(m)
                }
                for (ty in enumConstructor.paramTyAnnotations) {
                    childNodes.add(ty)
                }
            case enumDecl: EnumDecl =>
                for (a in enumDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in enumDecl.modifiers) {
                    childNodes.add(m)
                }
                for (param in enumDecl.genericParams) {
                    childNodes.add(param)
                }
                for (superTy in enumDecl.superTyAnnotations) {
                    childNodes.add(superTy)
                }
                if (let Some(constraints) <- enumDecl.genericConstraints) {
                    childNodes.add(constraints)
                }
                childNodes.add(enumDecl.body)
            case enumPattern: EnumPattern =>
                childNodes.add(enumPattern.enumConstructor)
                if (let Some(enumType) <- enumPattern.enumType) {
                    childNodes.add(enumType)
                }
                for (pattern in enumPattern.subPatterns) {
                    childNodes.add(pattern)
                }
            case extendDecl: ExtendDecl =>
                for (a in extendDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in extendDecl.modifiers) {
                    childNodes.add(m)
                }
                for (param in extendDecl.genericParams) {
                    childNodes.add(param)
                }
                childNodes.add(extendDecl.extendedTyAnnotation)
                for (superTy in extendDecl.superTyAnnotations) {
                    childNodes.add(superTy)
                }
                if (let Some(constraints) <- extendDecl.genericConstraints) {
                    childNodes.add(constraints)
                }
                childNodes.add(extendDecl.body)
            case forInExpr: ForInExpr =>
                childNodes.add(forInExpr.pattern)
                childNodes.add(forInExpr.expr)
                if (let Some(guard) <- forInExpr.patternGuard) {
                    childNodes.add(guard)
                }
                childNodes.add(forInExpr.body)
            case funcDecl: FuncDecl =>
                for (a in funcDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in funcDecl.modifiers) {
                    childNodes.add(m)
                }
                for (param in funcDecl.genericParams) {
                    childNodes.add(param)
                }
                childNodes.add(funcDecl.params)
                if (let Some(retTy) <- funcDecl.retTyAnnotation) {
                    childNodes.add(retTy)
                }
                if (let Some(constraints) <- funcDecl.genericConstraints) {
                    childNodes.add(constraints)
                }
                if (let Some(body) <- funcDecl.body) {
                    childNodes.add(body)
                }
            case funcParam: FuncParam =>
                for (a in funcParam.annotations) {
                    childNodes.add(a)
                }
                for (m in funcParam.modifiers) {
                    childNodes.add(m)
                }
                childNodes.add(funcParam.typeAnnotation)
                if (let Some(defaultValue) <- funcParam.defaultValue) {
                    childNodes.add(defaultValue)
                }
            case funcType: FuncType =>
                for (ty in funcType.paramTypes) {
                    childNodes.add(ty)
                }
                childNodes.add(funcType.retType)
            case genericConstraint: GenericConstraint =>
                childNodes.add(genericConstraint.typeArgument)
                for (bound in genericConstraint.upperBounds) {
                    childNodes.add(bound)
                }
            case genericConstraints: GenericConstraints => for (constraint in genericConstraints.constraints) {
                childNodes.add(constraint)
            }
            case genericParam: GenericParam =>
                for (a in genericParam.annotations) {
                    childNodes.add(a)
                }
                for (m in genericParam.modifiers) {
                    childNodes.add(m)
                }
            case ifExpr: IfExpr =>
                childNodes.add(ifExpr.condition)
                childNodes.add(ifExpr.ifBlock)
                if (let Some(elseIf) <- ifExpr.elseIf) {
                    childNodes.add(elseIf)
                }
                if (let Some(elseBlock) <- ifExpr.elseBlock) {
                    childNodes.add(elseBlock)
                }
            case importList: ImportList =>
                if (let Some(modifier) <- importList.modifier) {
                    childNodes.add(modifier)
                }
                childNodes.add(importList.contents)
            case importMulti: ImportMulti => for (content in importMulti.contents) {
                childNodes.add(content)
            }
            case incOrDecExpr: IncOrDecExpr => childNodes.add(incOrDecExpr.operand)
            case interfaceDecl: InterfaceDecl =>
                for (a in interfaceDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in interfaceDecl.modifiers) {
                    childNodes.add(m)
                }
                for (param in interfaceDecl.genericParams) {
                    childNodes.add(param)
                }
                for (superTy in interfaceDecl.superTyAnnotations) {
                    childNodes.add(superTy)
                }
                if (let Some(constraints) <- interfaceDecl.genericConstraints) {
                    childNodes.add(constraints)
                }
                childNodes.add(interfaceDecl.body)
            case isExpr: IsExpr =>
                childNodes.add(isExpr.srcVal)
                childNodes.add(isExpr.targetTypeAnnotation)
            case lambda: Lambda =>
                childNodes.add(lambda.params)
                for (n in lambda.body) {
                    childNodes.add(n)
                }
            case lambdaParam: LambdaParam =>
                for (a in lambdaParam.annotations) {
                    childNodes.add(a)
                }
                for (m in lambdaParam.modifiers) {
                    childNodes.add(m)
                }
                if (let Some(tyAnnotation) <- lambdaParam.typeAnnotation) {
                    childNodes.add(tyAnnotation)
                }
            case letPattern: LetPattern =>
                for (pattern in letPattern.patterns) {
                    childNodes.add(pattern)
                }
                childNodes.add(letPattern.expr)
            case litConstStrExpr: LitConstStrExpr => for (part in litConstStrExpr.strPartExprs) {
                match (part) {
                    case StrLiteralPart.LitConstPart(lit) => childNodes.add(lit)
                    case StrLiteralPart.StrInterpolation(strInterpolation) => childNodes.add(strInterpolation)
                    case _ => throw Exception()
                }
            }
            case macroDecl: MacroDecl =>
                for (a in macroDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in macroDecl.modifiers) {
                    childNodes.add(m)
                }
                childNodes.add(macroDecl.params)
                if (let Some(retTy) <- macroDecl.retTyAnnotation) {
                    childNodes.add(retTy)
                }
                childNodes.add(macroDecl.body)
            case macroExpandDecl: MacroExpandDecl =>
                for (a in macroExpandDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in macroExpandDecl.modifiers) {
                    childNodes.add(m)
                }
                childNodes.add(macroExpandDecl.calleeMacro)
                match (macroExpandDecl.macroInputs) {
                    case MacroExpandInput.WithoutParens(decl) => childNodes.add(decl)
                    case MacroExpandInput.WithParens(_) => return
                    case _ => throw Exception()
                }
            case macroExpandExpr: MacroExpandExpr =>
                childNodes.add(macroExpandExpr.calleeMacro)
                match (macroExpandExpr.macroInputs) {
                    case MacroExpandInput.WithoutParens(decl) => childNodes.add(decl)
                    case MacroExpandInput.WithParens(_) => return
                    case _ => throw Exception()
                }
            case macroExpandParam: MacroExpandParam =>
                for (a in macroExpandParam.annotations) {
                    childNodes.add(a)
                }
                for (m in macroExpandParam.modifiers) {
                    childNodes.add(m)
                }
                childNodes.add(macroExpandParam.calleeMacro)
                match (macroExpandParam.macroInputs) {
                    case MacroExpandInput.WithoutParens(decl) => childNodes.add(decl)
                    case MacroExpandInput.WithParens(_) => return
                    case _ => throw Exception()
                }
            case mainDecl: MainDecl =>
                for (a in mainDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in mainDecl.modifiers) {
                    childNodes.add(m)
                }
                childNodes.add(mainDecl.params)
                if (let Some(retTy) <- mainDecl.retTyAnnotation) {
                    childNodes.add(retTy)
                }
                childNodes.add(mainDecl.body)
            case matchCase: MatchCase =>
                if (let Some(caseCond) <- matchCase.caseCond) {
                    childNodes.add(caseCond)
                }
                for (pattern in matchCase.patterns) {
                    childNodes.add(pattern)
                }
                if (let Some(patternGuardCond) <- matchCase.patternGuardCond) {
                    childNodes.add(patternGuardCond)
                }
                for (n in matchCase.body) {
                    childNodes.add(n)
                }
            case matchExpr: MatchExpr =>
                if (let Some(selector) <- matchExpr.selector) {
                    childNodes.add(selector)
                }
                for (mCase in matchExpr.matchCases) {
                    childNodes.add(mCase)
                }
            case memberAccess: MemberAccess =>
                childNodes.add(memberAccess.base)
                childNodes.add(memberAccess.field)
            case optionalExpr: OptionalExpr => childNodes.add(optionalExpr.base)
            case packageNode: Package => for (file in packageNode.srcFile) {
                childNodes.add(file)
            }
            case packageHeader: PackageHeader =>
                if (let Some(accessModifier) <- packageHeader.accessModifier) {
                    childNodes.add(accessModifier)
                }
            case parameterList: ParameterList => for (param in parameterList.params) {
                childNodes.add(param)
            }
            case parenCondition: ParenCondition => childNodes.add(parenCondition.cond)
            case parenExpr: ParenExpr => childNodes.add(parenExpr.subExpr)
            case parenType: ParenType => childNodes.add(parenType.subType)
            case prefixType: PrefixType => childNodes.add(prefixType.base)
            case propDecl: PropDecl =>
                for (a in propDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in propDecl.modifiers) {
                    childNodes.add(m)
                }
                childNodes.add(propDecl.tyAnnotation)
                if (let Some(getter) <- propDecl.getter) {
                    childNodes.add(getter)
                }
                if (let Some(setter) <- propDecl.setter) {
                    childNodes.add(setter)
                }
            case propGetterOrSetter: PropGetterOrSetter =>
                for (a in propGetterOrSetter.annotations) {
                    childNodes.add(a)
                }
                for (m in propGetterOrSetter.modifiers) {
                    childNodes.add(m)
                }
                childNodes.add(propGetterOrSetter.block)
            case quoteExpr: QuoteExpr => for (content in quoteExpr.tokensOrRefExpr) {
                match (content) {
                    case QuoteExprContent.TokenPart(tks) => childNodes.add(tks)
                    case QuoteExprContent.QuoteInterpolation(quoteInterpolation) => childNodes.add(quoteInterpolation)
                    case _ => throw Exception()
                }
            }
            case quoteInterpolationExpr: QuoteInterpolationExpr => childNodes.add(quoteInterpolationExpr.expr)
            case rangeExpr: RangeExpr =>
                if (let Some(start) <- rangeExpr.start) {
                    childNodes.add(start)
                }
                if (let Some(end) <- rangeExpr.end) {
                    childNodes.add(end)
                }
                if (let Some(step) <- rangeExpr.step) {
                    childNodes.add(step)
                }
            case returnExpr: ReturnExpr =>
                if (let Some(retVal) <- returnExpr.retVal) {
                    childNodes.add(retVal)
                }
            case sourceFile: SourceFile =>
                if (let Some(pkgHeader) <- sourceFile.pkgHeader) {
                    childNodes.add(pkgHeader)
                }
                for (importList in sourceFile.importLists) {
                    childNodes.add(importList)
                }
                for (decl in sourceFile.topLevelDecls) {
                    childNodes.add(decl)
                }
                if (let Some(ftr) <- sourceFile.ftrDirective) {
                    childNodes.add(ftr)
                }
            case spawnExpr: SpawnExpr =>
                if (let Some(threadContext) <- spawnExpr.threadContext) {
                    childNodes.add(threadContext)
                }
                childNodes.add(spawnExpr.trailingLambdaExpr)
            case staticInit: StaticInit =>
                for (a in staticInit.annotations) {
                    childNodes.add(a)
                }
                for (m in staticInit.modifiers) {
                    childNodes.add(m)
                }
                childNodes.add(staticInit.body)
            case strInterpolationContent: StrInterpolationContent => childNodes.add(
                strInterpolationContent.interpolationBlock)
            case structDecl: StructDecl =>
                for (a in structDecl.annotations) {
                    childNodes.add(a)
                }
                for (m in structDecl.modifiers) {
                    childNodes.add(m)
                }
                for (param in structDecl.genericParams) {
                    childNodes.add(param)
                }
                for (superTy in structDecl.superTyAnnotations) {
                    childNodes.add(superTy)
                }
                if (let Some(constraints) <- structDecl.genericConstraints) {
                    childNodes.add(constraints)
                }
                childNodes.add(structDecl.body)
            case subscriptExpr: SubscriptExpr =>
                childNodes.add(subscriptExpr.base)
                for (index in subscriptExpr.indexs) {
                    childNodes.add(index)
                }
            case throwExpr: ThrowExpr => childNodes.add(throwExpr.throwVal)
            case trailingClosureExpr: TrailingClosureExpr =>
                childNodes.add(trailingClosureExpr.callee)
                for (arg in trailingClosureExpr.arguments) {
                    childNodes.add(arg)
                }
                childNodes.add(trailingClosureExpr.trailingLambdaExpr)
            case tryCatch: TryCatch =>
                for (varDecl in tryCatch.resourceSpec) {
                    childNodes.add(varDecl)
                }
                childNodes.add(tryCatch.tryBlock)
                // The number of catchPatterns and catchBlocks must be equal for a valid TryCatch Node.
                for (i in 0..tryCatch.catchPatterns.size) {
                    childNodes.add(tryCatch.catchPatterns[i])
                    childNodes.add(tryCatch.catchBlocks[i])
                }
                if (let Some(finallyBlock) <- tryCatch.finallyBlock) {
                    childNodes.add(finallyBlock)
                }
            case tupleLiteral: TupleLiteral => for (element in tupleLiteral.elements) {
                childNodes.add(element)
            }
            case tuplePattern: TuplePattern => for (pattern in tuplePattern.subPatterns) {
                childNodes.add(pattern)
            }
            case tupleType: TupleType => for (element in tupleType.elements) {
                childNodes.add(element)
            }
            case typeAlias: TypeAlias =>
                for (a in typeAlias.annotations) {
                    childNodes.add(a)
                }
                for (m in typeAlias.modifiers) {
                    childNodes.add(m)
                }
                for (param in typeAlias.typeParameters) {
                    childNodes.add(param)
                }
                childNodes.add(typeAlias.originalTyAnnotation)
            case typeConvExpr: TypeConvExpr =>
                childNodes.add(typeConvExpr.targetTypeAnnotation)
                childNodes.add(typeConvExpr.srcVal)
            case typePattern: TypePattern =>
                childNodes.add(typePattern.subPattern)
                childNodes.add(typePattern.patternType)
            case unaryExpr: UnaryExpr => childNodes.add(unaryExpr.operand)
            case unsafeExpr: UnsafeExpr => childNodes.add(unsafeExpr.block)
            case varrayExpr: VArrayExpr =>
                childNodes.add(varrayExpr.vArrayType)
                childNodes.add(varrayExpr.argument)
            case varrayType: VArrayType => childNodes.add(varrayType.elementType)
            case whileExpr: WhileExpr =>
                childNodes.add(whileExpr.condition)
                childNodes.add(whileExpr.body)
            case synchronizedExpr: SynchronizedExpr =>
                childNodes.add(synchronizedExpr.structuredMutex)
                childNodes.add(synchronizedExpr.block)
            case featuresDirective: FeaturesDirective =>
                for (a in featuresDirective.annotations) {
                    childNodes.add(a)
                }
                childNodes.add(featuresDirective.featuresSet)
            case featuresSet: FeaturesSet =>
                for (f in featuresSet.content) {
                    childNodes.add(f)
                }
            case _ => return
            // For SyntaxTreeNode without child nodes, do not add anything
        }
    }

    func getChildNodes(): ArrayList<SyntaxTreeNode> {
        return childNodes
    }

    func clearChildNodes(): Unit {
        childNodes.clear()
    }
}