/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * This source file is part of the Cangjie project, licensed under Apache-2.0
 * with Runtime Library Exception.
 *
 * See https://cangjie-lang.cn/pages/LICENSE for license information.
 */

package stdx.syntax

public open class ASTVisitor {
    private var isStop = false
    public func walk(root: SyntaxTreeNode): Unit {
        if (isStop) {
            return
        }
        match (preAction(root)) {
            case Continue => visit(root)
            case Skip => return
            case Stop =>
                isStop = true
                return
        }
        if (isStop) {
            return
        }
        match (postAction(root)) {
            case Continue => return
            case Stop =>
                isStop = true
                return
        }
    }

    private func visit(node: SyntaxTreeNode) {
        for (comment in node.comments) {
            walk(comment)
        }
        match (node) {
            case binaryExpr: BinaryExpr => visitBinaryExpr(binaryExpr)
            case varDecl: VarDecl => visitVarDecl(varDecl)
            case symbolRef: SymbolRef => visitSymbolRef(symbolRef)
            case annotation: Annotation => visitAnnotation(annotation)
            case argument: Argument => visitArgument(argument)
            case arrayLiteral: ArrayLiteral => visitArrayLiteral(arrayLiteral)
            case asExpr: AsExpr => visitAsExpr(asExpr)
            case assignExpr: AssignExpr => visitAssignExpr(assignExpr)
            case atomicType: AtomicType => visitAtomicType(atomicType)
            case block: Block => visitBlock(block)
            case body: Body => visitBody(body)
            case breakExpr: BreakExpr => visitBreakExpr(breakExpr)
            case callExpr: CallExpr => visitCallExpr(callExpr)
            case catchPattern: CatchPattern => visitCatchPattern(catchPattern)
            case classDecl: ClassDecl => visitClassDecl(classDecl)
            case comment: Comment => return
            case compositeType: CompositeType => visitCompositeType(compositeType)
            case conjunctionCondition: ConjunctionCondition => visitConjunctionCondition(conjunctionCondition)
            case constPattern: ConstPattern => visitConstPattern(constPattern)
            case continueExpr: ContinueExpr => visitContinueExpr(continueExpr)
            case disjunctionCondition: DisjunctionCondition => visitDisjunctionCondition(disjunctionCondition)
            case doWhileExpr: DoWhileExpr => visitDoWhileExpr(doWhileExpr)
            case enumConstructor: EnumConstructor => visitEnumConstructor(enumConstructor)
            case enumDecl: EnumDecl => visitEnumDecl(enumDecl)
            case enumPattern: EnumPattern => visitEnumPattern(enumPattern)
            case extendDecl: ExtendDecl => visitExtendDecl(extendDecl)
            case forInExpr: ForInExpr => visitForInExpr(forInExpr)
            case funcDecl: FuncDecl => visitFuncDecl(funcDecl)
            case funcParam: FuncParam => visitFuncParam(funcParam)
            case funcType: FuncType => visitFuncType(funcType)
            case genericConstraint: GenericConstraint => visitGenericConstraint(genericConstraint)
            case genericConstraints: GenericConstraints => visitGenericConstraints(genericConstraints)
            case genericParam: GenericParam => visitGenericParam(genericParam)
            case ifExpr: IfExpr => visitIfExpr(ifExpr)
            case importAlias: ImportAlias => visitImportAlias(importAlias)
            case importAll: ImportAll => visitImportAll(importAll)
            case importContent: ImportContent => visitImportContent(importContent)
            case importList: ImportList => visitImportList(importList)
            case importMulti: ImportMulti => visitImportMulti(importMulti)
            case importSingle: ImportSingle => visitImportSingle(importSingle)
            case incOrDecExpr: IncOrDecExpr => visitIncOrDecExpr(incOrDecExpr)
            case interfaceDecl: InterfaceDecl => visitInterfaceDecl(interfaceDecl)
            case isExpr: IsExpr => visitIsExpr(isExpr)
            case lambda: Lambda => visitLambda(lambda)
            case lambdaParam: LambdaParam => visitLambdaParam(lambdaParam)
            case letPattern: LetPattern => visitLetPattern(letPattern)
            case litConstExpr: LitConstExpr => visitLitConstExpr(litConstExpr)
            case litConstRuneExpr: LitConstRuneExpr => visitLitConstRuneExpr(litConstRuneExpr)
            case litConstStrExpr: LitConstStrExpr => visitLitConstStrExpr(litConstStrExpr)
            case macroDecl: MacroDecl => visitMacroDecl(macroDecl)
            case macroExpandDecl: MacroExpandDecl => visitMacroExpandDecl(macroExpandDecl)
            case macroExpandExpr: MacroExpandExpr => visitMacroExpandExpr(macroExpandExpr)
            case macroExpandParam: MacroExpandParam => visitMacroExpandParam(macroExpandParam)
            case mainDecl: MainDecl => visitMainDecl(mainDecl)
            case matchCase: MatchCase => visitMatchCase(matchCase)
            case matchExpr: MatchExpr => visitMatchExpr(matchExpr)
            case memberAccess: MemberAccess => visitMemberAccess(memberAccess)
            case modifier: Modifier => visitModifier(modifier)
            case optionalExpr: OptionalExpr => visitOptionalExpr(optionalExpr)
            case packageNode: Package => visitPackage(packageNode)
            case packageHeader: PackageHeader => visitPackageHeader(packageHeader)
            case parameterList: ParameterList => visitParameterList(parameterList)
            case parenCondition: ParenCondition => visitParenCondition(parenCondition)
            case parenExpr: ParenExpr => visitParenExpr(parenExpr)
            case parenType: ParenType => visitParenType(parenType)
            case prefixType: PrefixType => visitPrefixType(prefixType)
            case propDecl: PropDecl => visitPropDecl(propDecl)
            case propGetterOrSetter: PropGetterOrSetter => visitPropGetterOrSetter(propGetterOrSetter)
            case quoteExpr: QuoteExpr => visitQuoteExpr(quoteExpr)
            case quoteInterpolationExpr: QuoteInterpolationExpr => visitQuoteInterpolationExpr(quoteInterpolationExpr)
            case quoteToken: QuoteToken => visitQuoteToken(quoteToken)
            case rangeExpr: RangeExpr => visitRangeExpr(rangeExpr)
            case returnExpr: ReturnExpr => visitReturnExpr(returnExpr)
            case sourceFile: SourceFile => visitSourceFile(sourceFile)
            case spawnExpr: SpawnExpr => visitSpawnExpr(spawnExpr)
            case staticInit: StaticInit => visitStaticInit(staticInit)
            case strInterpolationContent: StrInterpolationContent => visitStrInterpolationContent(
                strInterpolationContent)
            case structDecl: StructDecl => visitStructDecl(structDecl)
            case subscriptExpr: SubscriptExpr => visitSubscriptExpr(subscriptExpr)
            case throwExpr: ThrowExpr => visitThrowExpr(throwExpr)
            case trailingClosureExpr: TrailingClosureExpr => visitTrailingClosureExpr(trailingClosureExpr)
            case tryCatch: TryCatch => visitTryCatch(tryCatch)
            case tupleLiteral: TupleLiteral => visitTupleLiteral(tupleLiteral)
            case tuplePattern: TuplePattern => visitTuplePattern(tuplePattern)
            case tupleType: TupleType => visitTupleType(tupleType)
            case typeAlias: TypeAlias => visitTypeAlias(typeAlias)
            case typeConvExpr: TypeConvExpr => visitTypeConvExpr(typeConvExpr)
            case typePattern: TypePattern => visitTypePattern(typePattern)
            case unaryExpr: UnaryExpr => visitUnaryExpr(unaryExpr)
            case unsafeExpr: UnsafeExpr => visitUnsafeExpr(unsafeExpr)
            case varOrEnumPattern: VarOrEnumPattern => visitVarOrEnumPattern(varOrEnumPattern)
            case varPattern: VarPattern => visitVarPattern(varPattern)
            case varrayExpr: VArrayExpr => visitVArrayExpr(varrayExpr)
            case varrayType: VArrayType => visitVArrayType(varrayType)
            case whileExpr: WhileExpr => visitWhileExpr(whileExpr)
            case wildcardPattern: WildcardPattern => visitWildcardPattern(wildcardPattern)
            case synchronizedExpr: SynchronizedExpr => visitSynchronizedExpr(synchronizedExpr)
            case featuresDirective: FeaturesDirective => visitFeaturesDirective(featuresDirective)
            case featuresSet: FeaturesSet => visitFeaturesSet(featuresSet)
            case featureId: FeatureId => visitFeatureId(featureId)
            case _ => throw Exception()
        }
    }

    private func walkAll<T>(nodes: Array<T>) where T <: SyntaxTreeNode {
        for (node in nodes) {
            walk(node)
        }
    }

    private func walkOptional<T>(node: Option<T>) where T <: SyntaxTreeNode {
        if (let Some(v) <- node) {
            walk(v)
        }
    }

    private func visitMacroOrMainDeclChildren(annotations: Array<Annotation>, modifiers: Array<Modifier>,
        params: ParameterList, retTyAnnotation: Option<TypeAnnotation>, body: Block) {
        for (a in annotations) {
            walk(a)
        }
        for (m in modifiers) {
            walk(m)
        }
        walk(params)
        if (let Some(retTy) <- retTyAnnotation) {
            walk(retTy)
        }
        walk(body)
    }

    private func visitMacroExpandDeclOrParamChildren(annotations: Array<Annotation>, modifiers: Array<Modifier>,
        calleeMacro: Expr, macroInputs: MacroExpandInput) {
        for (a in annotations) {
            walk(a)
        }
        for (m in modifiers) {
            walk(m)
        }
        walk(calleeMacro)
        // macroAttrs is Tokens, no need to walk
        match (macroInputs) {
            case MacroExpandInput.WithoutParens(decl) => walk(decl)
            case MacroExpandInput.WithParens(_) => return
            case _ => throw Exception("VisitException: Failed to match the enumeration value")
        }
    }

    private func visitBinaryExpr(node: BinaryExpr) {
        walk(node.lhs)
        walk(node.rhs)
    }

    private func visitVarDecl(node: VarDecl) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
        walk(node.pattern)
        if (let Some(tyAnnotation) <- node.tyAnnotation) {
            walk(tyAnnotation)
        }
        if (let Some(initializer) <- node.initializer) {
            walk(initializer)
        }
    }

    private func visitSymbolRef(node: SymbolRef) {
        for (t in node.typeArguments) {
            walk(t)
        }
    }

    private func visitAnnotation(node: Annotation) {
        for (arg in node.arguments) {
            walk(arg)
        }
    }

    private func visitArgument(node: Argument) {
        walk(node.value)
    }

    private func visitArrayLiteral(node: ArrayLiteral) {
        for (element in node.elements) {
            walk(element)
        }
    }

    private func visitAsExpr(node: AsExpr) {
        walk(node.srcVal)
        walk(node.targetTypeAnnotation)
    }

    private func visitAssignExpr(node: AssignExpr) {
        walk(node.lhs)
        walk(node.rhs)
    }

    private func visitAtomicType(node: AtomicType) {
        // kind is AtomicTypeKind, no need to walk
    }

    private func visitBlock(node: Block) {
        for (n in node.nodes) {
            walk(n)
        }
    }

    private func visitBody(node: Body) {
        for (decl in node.memberDecls) {
            walk(decl)
        }
    }

    private func visitBreakExpr(node: BreakExpr) {
        // no child nodes to walk
    }

    private func visitCallExpr(node: CallExpr) {
        walk(node.callee)
        for (arg in node.arguments) {
            walk(arg)
        }
    }

    private func visitCatchPattern(node: CatchPattern) {
        walk(node.pattern)
        for (ty in node.exceptionType) {
            walk(ty)
        }
    }

    private func visitClassDecl(node: ClassDecl) {
        walkAll(node.annotations)
        walkAll(node.modifiers)
        walkAll(node.genericParams)
        walkAll(node.superTyAnnotations)
        walkOptional(node.genericConstraints)
        walk(node.body)
    }

    private func visitCompositeType(node: CompositeType) {
        for (arg in node.typeArguments) {
            walk(arg)
        }
    }

    private func visitConjunctionCondition(node: ConjunctionCondition) {
        for (cond in node.cond) {
            match (cond) {
                case AtomicCondition.LetPatternCondition(letPattern) => walk(letPattern)
                case AtomicCondition.Expression(expr) => walk(expr)
                case AtomicCondition.ParenConditionConstructor(parenCondition) => walk(parenCondition)
                case _ => throw Exception("VisitException: Failed to match the enumeration value")
            }
        }
    }

    private func visitConstPattern(node: ConstPattern) {
        walk(node.litConstExpr)
    }

    private func visitContinueExpr(node: ContinueExpr) {
        // no child nodes to walk
    }

    private func visitDisjunctionCondition(node: DisjunctionCondition) {
        for (cond in node.cond) {
            walk(cond)
        }
    }

    private func visitDoWhileExpr(node: DoWhileExpr) {
        walk(node.body)
        walk(node.condition)
    }

    private func visitEnumConstructor(node: EnumConstructor) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
        for (ty in node.paramTyAnnotations) {
            walk(ty)
        }
    }

    private func visitEnumDecl(node: EnumDecl) {
        walkAll(node.annotations)
        walkAll(node.modifiers)
        walkAll(node.genericParams)
        walkAll(node.superTyAnnotations)
        walkOptional(node.genericConstraints)
        walk(node.body)
    }

    private func visitEnumPattern(node: EnumPattern) {
        walk(node.enumConstructor)
        if (let Some(enumType) <- node.enumType) {
            walk(enumType)
        }
        for (pattern in node.subPatterns) {
            walk(pattern)
        }
    }

    private func visitExtendDecl(node: ExtendDecl) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
        for (param in node.genericParams) {
            walk(param)
        }
        walk(node.extendedTyAnnotation)
        for (superTy in node.superTyAnnotations) {
            walk(superTy)
        }
        if (let Some(constraints) <- node.genericConstraints) {
            walk(constraints)
        }
        walk(node.body)
    }

    private func visitForInExpr(node: ForInExpr) {
        walk(node.pattern)
        walk(node.expr)
        if (let Some(guard) <- node.patternGuard) {
            walk(guard)
        }
        walk(node.body)
    }

    private func visitFuncDecl(node: FuncDecl) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
        for (param in node.genericParams) {
            walk(param)
        }
        if (let Some(constraints) <- node.genericConstraints) {
            walk(constraints)
        }
        walk(node.params)
        if (let Some(retTy) <- node.retTyAnnotation) {
            walk(retTy)
        }
        if (let Some(body) <- node.body) {
            walk(body)
        }
    }

    private func visitFuncParam(node: FuncParam) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
        walk(node.typeAnnotation)
        if (let Some(defaultValue) <- node.defaultValue) {
            walk(defaultValue)
        }
    }

    private func visitFuncType(node: FuncType) {
        for (ty in node.paramTypes) {
            walk(ty)
        }
        walk(node.retType)
    }

    private func visitGenericConstraint(node: GenericConstraint) {
        walk(node.typeArgument)
        for (bound in node.upperBounds) {
            walk(bound)
        }
    }

    private func visitGenericConstraints(node: GenericConstraints) {
        for (constraint in node.constraints) {
            walk(constraint)
        }
    }

    private func visitGenericParam(node: GenericParam) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
    }

    private func visitIfExpr(node: IfExpr) {
        walk(node.condition)
        walk(node.ifBlock)
        if (let Some(elseIf) <- node.elseIf) {
            walk(elseIf)
        }
        if (let Some(elseBlock) <- node.elseBlock) {
            walk(elseBlock)
        }
    }

    private func visitImportAlias(node: ImportAlias) {
        // aliasName and identifier are String, no need to walk
    }

    private func visitImportAll(node: ImportAll) {
        // prefix is String, no need to walk
    }

    private func visitImportContent(node: ImportContent) {
        // prefix is String, no need to walk
    }

    private func visitImportList(node: ImportList) {
        if (let Some(modifier) <- node.modifier) {
            walk(modifier)
        }
        walk(node.contents)
    }

    private func visitImportMulti(node: ImportMulti) {
        for (content in node.contents) {
            walk(content)
        }
    }

    private func visitImportSingle(node: ImportSingle) {
        // identifier is String, no need to walk
    }

    private func visitIncOrDecExpr(node: IncOrDecExpr) {
        walk(node.operand)
    }

    private func visitInterfaceDecl(node: InterfaceDecl) {
        walkAll(node.annotations)
        walkAll(node.modifiers)
        walkAll(node.genericParams)
        walkAll(node.superTyAnnotations)
        walkOptional(node.genericConstraints)
        walk(node.body)
    }

    private func visitIsExpr(node: IsExpr) {
        walk(node.srcVal)
        walk(node.targetTypeAnnotation)
    }

    private func visitLambda(node: Lambda) {
        walk(node.params)
        for (n in node.body) {
            walk(n)
        }
    }

    private func visitLambdaParam(node: LambdaParam) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
        if (let Some(tyAnnotation) <- node.typeAnnotation) {
            walk(tyAnnotation)
        }
    }

    private func visitLetPattern(node: LetPattern) {
        for (pattern in node.patterns) {
            walk(pattern)
        }
        walk(node.expr)
    }

    private func visitLitConstExpr(node: LitConstExpr) {
        // kind and rawValue are primitive types, no need to walk
    }

    private func visitLitConstRuneExpr(node: LitConstRuneExpr) {
        // isSingleQuote is Bool, no need to walk
    }

    private func visitLitConstStrExpr(node: LitConstStrExpr) {
        // delimiterNum, isSingleQuote, strKind are primitive types
        for (part in node.strPartExprs) {
            match (part) {
                case StrLiteralPart.LitConstPart(lit) => walk(lit)
                case StrLiteralPart.StrInterpolation(strInterpolation) => walk(strInterpolation)
                case _ => throw Exception("VisitException: Failed to match the enumeration value")
            }
        }
    }

    private func visitMacroDecl(node: MacroDecl) {
        visitMacroOrMainDeclChildren(node.annotations, node.modifiers, node.params, node.retTyAnnotation, node.body)
    }

    private func visitMacroExpandDecl(node: MacroExpandDecl) {
        visitMacroExpandDeclOrParamChildren(node.annotations, node.modifiers, node.calleeMacro, node.macroInputs)
    }

    private func visitMacroExpandExpr(node: MacroExpandExpr) {
        walk(node.calleeMacro)
        // macroAttrs is Tokens, no need to walk
        match (node.macroInputs) {
            case MacroExpandInput.WithoutParens(decl) => walk(decl)
            case MacroExpandInput.WithParens(_) => return
            case _ => throw Exception("VisitException: Failed to match the enumeration value")
        }
    }

    private func visitMacroExpandParam(node: MacroExpandParam) {
        visitMacroExpandDeclOrParamChildren(node.annotations, node.modifiers, node.calleeMacro, node.macroInputs)
    }

    private func visitMainDecl(node: MainDecl) {
        visitMacroOrMainDeclChildren(node.annotations, node.modifiers, node.params, node.retTyAnnotation, node.body)
    }

    private func visitMatchCase(node: MatchCase) {
        if (let Some(caseCond) <- node.caseCond) {
            walk(caseCond)
        }
        for (pattern in node.patterns) {
            walk(pattern)
        }
        if (let Some(patternGuardCond) <- node.patternGuardCond) {
            walk(patternGuardCond)
        }
        for (n in node.body) {
            walk(n)
        }
    }

    private func visitMatchExpr(node: MatchExpr) {
        if (let Some(selector) <- node.selector) {
            walk(selector)
        }
        for (mCase in node.matchCases) {
            walk(mCase)
        }
    }

    private func visitMemberAccess(node: MemberAccess) {
        walk(node.base)
        walk(node.field)
    }

    private func visitModifier(node: Modifier) {
        // kind is ModifierKind, no need to walk
    }

    private func visitOptionalExpr(node: OptionalExpr) {
        walk(node.base)
    }

    private func visitPackage(node: Package) {
        // name is String, isMacroPkg is Bool, no need to walk
        for (file in node.srcFile) {
            walk(file)
        }
    }

    private func visitPackageHeader(node: PackageHeader) {
        if (let Some(accessModifier) <- node.accessModifier) {
            walk(accessModifier)
        }
        // isMacroPkg is Bool, packageNameIdentifiers are String
    }

    private func visitParameterList(node: ParameterList) {
        for (param in node.params) {
            walk(param)
        }
    }

    private func visitParenCondition(node: ParenCondition) {
        walk(node.cond)
    }

    private func visitParenExpr(node: ParenExpr) {
        walk(node.subExpr)
    }

    private func visitParenType(node: ParenType) {
        walk(node.subType)
    }

    private func visitPrefixType(node: PrefixType) {
        walk(node.base)
        // prefixTypeOpKind is PrefixTypeOpKind, no need to walk
    }

    private func visitPropDecl(node: PropDecl) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
        // name is String, isMut is Bool, no need to walk
        walk(node.tyAnnotation)
        if (let Some(getter) <- node.getter) {
            walk(getter)
        }
        if (let Some(setter) <- node.setter) {
            walk(setter)
        }
    }

    private func visitPropGetterOrSetter(node: PropGetterOrSetter) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
        walk(node.block)
    }

    private func visitQuoteExpr(node: QuoteExpr) {
        for (content in node.tokensOrRefExpr) {
            match (content) {
                case QuoteExprContent.TokenPart(tks) => walk(tks)
                case QuoteExprContent.QuoteInterpolation(quoteInterpolation) => walk(quoteInterpolation)
                case _ => throw Exception("VisitException: Failed to match the enumeration value")
            }
        }
    }

    private func visitQuoteInterpolationExpr(node: QuoteInterpolationExpr) {
        walk(node.expr)
    }

    private func visitQuoteToken(node: QuoteToken) {
        // content is Tokens, no need to walk
    }

    private func visitRangeExpr(node: RangeExpr) {
        if (let Some(start) <- node.start) {
            walk(start)
        }
        if (let Some(end) <- node.end) {
            walk(end)
        }
        if (let Some(step) <- node.step) {
            walk(step)
        }
    }

    private func visitReturnExpr(node: ReturnExpr) {
        if (let Some(retVal) <- node.retVal) {
            walk(retVal)
        }
    }

    private func visitSourceFile(node: SourceFile) {
        // name and path are String, no need to walk
        if (let Some(pkgHeader) <- node.pkgHeader) {
            walk(pkgHeader)
        }
        for (importList in node.importLists) {
            walk(importList)
        }
        for (decl in node.topLevelDecls) {
            walk(decl)
        }
    }

    private func visitSpawnExpr(node: SpawnExpr) {
        if (let Some(threadContext) <- node.threadContext) {
            walk(threadContext)
        }
        walk(node.trailingLambdaExpr)
    }

    private func visitStaticInit(node: StaticInit) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
        walk(node.body)
    }

    private func visitStrInterpolationContent(node: StrInterpolationContent) {
        walk(node.interpolationBlock)
    }

    private func visitStructDecl(node: StructDecl) {
        walkAll(node.annotations)
        walkAll(node.modifiers)
        walkAll(node.genericParams)
        walkAll(node.superTyAnnotations)
        walkOptional(node.genericConstraints)
        walk(node.body)
    }

    private func visitSubscriptExpr(node: SubscriptExpr) {
        walk(node.base)
        for (index in node.indexs) {
            walk(index)
        }
    }

    private func visitThrowExpr(node: ThrowExpr) {
        walk(node.throwVal)
    }

    private func visitTrailingClosureExpr(node: TrailingClosureExpr) {
        walk(node.callee)
        for (arg in node.arguments) {
            walk(arg)
        }
        walk(node.trailingLambdaExpr)
    }

    private func visitTryCatch(node: TryCatch) {
        for (varDecl in node.resourceSpec) {
            walk(varDecl)
        }
        walk(node.tryBlock)
        for (pattern in node.catchPatterns) {
            walk(pattern)
        }
        for (block in node.catchBlocks) {
            walk(block)
        }
        if (let Some(finallyBlock) <- node.finallyBlock) {
            walk(finallyBlock)
        }
    }

    private func visitTupleLiteral(node: TupleLiteral) {
        for (element in node.elements) {
            walk(element)
        }
    }

    private func visitTuplePattern(node: TuplePattern) {
        for (pattern in node.subPatterns) {
            walk(pattern)
        }
    }

    private func visitTupleType(node: TupleType) {
        for (element in node.elements) {
            walk(element)
        }
    }

    private func visitTypeAlias(node: TypeAlias) {
        for (a in node.annotations) {
            walk(a)
        }
        for (m in node.modifiers) {
            walk(m)
        }
        // aliasName is String, no need to walk
        for (param in node.typeParameters) {
            walk(param)
        }
        walk(node.originalTyAnnotation)
    }

    private func visitTypeConvExpr(node: TypeConvExpr) {
        walk(node.targetTypeAnnotation)
        walk(node.srcVal)
    }

    private func visitTypePattern(node: TypePattern) {
        walk(node.subPattern)
        walk(node.patternType)
    }

    private func visitUnaryExpr(node: UnaryExpr) {
        walk(node.operand)
        // opKind is UnaryOpKind, no need to walk
    }

    private func visitUnsafeExpr(node: UnsafeExpr) {
        walk(node.block)
    }

    private func visitVarOrEnumPattern(node: VarOrEnumPattern) {
        // identifier is String, no need to walk
    }

    private func visitVarPattern(node: VarPattern) {
        // name is String, no need to walk
    }

    private func visitVArrayExpr(node: VArrayExpr) {
        walk(node.argument)
        walk(node.vArrayType)
    }

    private func visitVArrayType(node: VArrayType) {
        walk(node.elementType)
        // size is Int64, no need to walk
    }

    private func visitWhileExpr(node: WhileExpr) {
        walk(node.condition)
        walk(node.body)
    }

    private func visitWildcardPattern(node: WildcardPattern) {
        // no child nodes to walk
    }

    private func visitSynchronizedExpr(node: SynchronizedExpr) {
        walk(node.structuredMutex)
        walk(node.block)
    }

    private func visitFeaturesDirective(node: FeaturesDirective) {
        for (a in node.annotations) {
            walk(a)
        }
        walk(node.featuresSet)
    }

    private func visitFeaturesSet(node: FeaturesSet) {
        for (f in node.content) {
            walk(f)
        }
        // content is Array<FeatureId>
    }

    private func visitFeatureId(node: FeatureId) {
        // featureNameIdentifiers is Array<String>, no need to walk
    }

    public open func preAction(node: SyntaxTreeNode): PreActionMode {
        return PreActionMode.Continue
    }

    public open func postAction(node: SyntaxTreeNode): PostActionMode {
        return PostActionMode.Continue
    }
}

public enum PreActionMode {
    | Continue
    | Skip
    | Stop
}

public enum PostActionMode {
    | Continue
    | Stop
}