/*
 * 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

import std.collection.ArrayList
import std.deriving.*
import std.ast.ToTokens

extend Token {
    func addPosition(pos: CodePosition) {
        this.addPosition(0, pos.line, pos.column)
    }
}
/**
 * @brief Abstract sealed class representing a node in the syntax tree.
 *
 * This class serves as a base for other syntax tree node classes.
 */
sealed abstract class SyntaxTreeNode <: ToString & ToTokens & Hashable & Equatable<SyntaxTreeNode> {
    /**
     * @brief The position of this node
     *
     * CodePosition range covering the start and end positions of the node in source code
     */
    public let nodePos: CodePositionRange

    /**
     * @brief The parent node of this node.
     *
     * This optional field may contain a reference to the parent node in the syntax tree.
     */
    public let parentNode: Option<SyntaxTreeNode>

    let nodeImpl: SyntaxNodeImpl

    let commentsPropInfo: Array<PropInfo>

    /**
     * @brief Returns a string representation of the node.
     *
     * @return A string representation of the node, or an empty string if no implementation is set.
     */
    public open func toString(): String {
        nodeImpl.preWhiteSpaceString() + nodeImpl.toString()
    }

    private func nonTerminalToToken(nt: NonTerminal, startPos: CodePosition): Token {
        var strVal = ""
        var isSingleQuote = false
        var delimiterNum: UInt16 = 1
        var sz = nt.children.size
        match (nt.children[sz - 1]) {
            case rt: RepeatedTerminal =>
                delimiterNum = UInt16(rt.repeat)
                sz -= 1
            case _ => ()
        }
        // sz is the position of quote
        if (nt.children[sz - 1].kind.toString() == "'" || nt.children[sz - 1].kind.toString() == "'''") {
            isSingleQuote = true
        }
        // get token kind for literal
        let tokenKind = match (nt.kind) {
            case SyntaxNodeKind.RuneLiteral => TokenKind.RUNE_LITERAL
            case SyntaxNodeKind.LineStringLiteral =>
                if (isSingleQuote) {
                    TokenKind.SINGLE_QUOTED_STRING_LITERAL
                } else {
                    TokenKind.STRING_LITERAL
                }
            case SyntaxNodeKind.MultiLineStringLiteral => TokenKind.MULTILINE_STRING
            case SyntaxNodeKind.MultiLineRawStringLiteral => TokenKind.MULTILINE_RAW_STRING
            case _ => TokenKind.ILLEGAL
        }
        // get value for string literal
        for (child in nt.children) {
            match (child) {
                case vt: ValuedTerminal => strVal += vt.value
                case nt: NonTerminal => strVal += nt.toString()
                case _ => ()
            }
        }
        var token = Token(tokenKind, strVal).addPosition(startPos)
        token.isSingleQuote = isSingleQuote
        token.delimiterNum = delimiterNum
        return token
    }

    func repeatedTerminalToTokens(rt: RepeatedTerminal, offset: Offset, startPos: CodePosition, tokenKind: TokenKind,
        tks: ArrayList<Token>) {
        for (_ in 0..rt.repeat) {
            let tk = Token(tokenKind).addPosition(startPos + offset)
            tks.add(tk)
            offset.moveString(tk.value)
        }
    }

    public open func toTokens(): Tokens {
        let startPos = nodePos.begin
        let nodeImplFlatter = nodeImpl.flatter()
        let sz = nodeImplFlatter.size
        let tks = ArrayList<Token>()
        var offset = Offset()
        for (i in 0..sz) {
            let tk = nodeImplFlatter[i]
            var token = Token()
            match (tk) {
                // literal special handling, these are NonTerminals that can be directly converted into Tokens.
                case nt: NonTerminal => token = nonTerminalToToken(nt, startPos + offset)
                case _ =>
                    // special handling for TokenTerminal
                    let tokenKind = if (let Some(tt) <- (tk as TokenTerminal)) {
                        tt.tokenKind
                    } else {
                        tk.kind.toTokenKind()
                    }
                    // skip illegal token like space
                    if (tokenKind == TokenKind.ILLEGAL) {
                        offset.moveNode(tk)
                        continue
                    }
                    // convert Terminal to Token
                    token = match (tk) {
                        case rt: RepeatedTerminal =>
                            repeatedTerminalToTokens(rt, offset, startPos, tokenKind, tks)
                            continue
                        case vt: ValuedTerminal => Token(tokenKind, vt.value)
                        case tt: TokenTerminal => Token(tt.tokenKind, tt.value)
                        case _ => Token(tokenKind, tk.toString())
                    }
                    token = token.addPosition(startPos + offset)
            }
            tks.add(token)
            offset.moveNode(tk)
        }
        Tokens(tks)
    }

    init(nodePos: CodePositionRange, nodeImpl: SyntaxNodeImpl, parentNode: ?SyntaxTreeNode,
        commentsPropInfo: Array<PropInfo>) {
        this.nodePos = nodePos
        this.nodeImpl = nodeImpl
        this.parentNode = parentNode
        this.commentsPropInfo = commentsPropInfo
    }

    // only support for initialize nodes with public Init Function
    init(nodeImpl: SyntaxNodeImpl, hasComment!: Bool = true) {
        let startPos = DEFAULT_START_POS
        this.nodePos = CodePositionRange(startPos, startPos + nodeImpl.offset)
        this.nodeImpl = nodeImpl
        this.parentNode = None
        if (hasComment) {
            this.commentsPropInfo = [PropInfo()]
        } else {
            this.commentsPropInfo = Array<PropInfo>()
        }
    }

    func transTokenList(node: SyntaxNodeImpl): Tokens {
        let content = ArrayList<Token>()
        if (let Some(nt) <- (node as NonTerminal)) {
            for (child in nt.children) {
                if (let Some(tk) <- (child as TokenTerminal)) {
                    content.add(Token(tk.tokenKind, tk.value))
                }
            }
        }
        Tokens(content)
    }

    /**
     * @brief Judge whether current node is equal to the other node.
     *
     * @return Boolean value representing the equality of two nodes.
     */
    public operator func ==(that: SyntaxTreeNode): Bool {
        if (this.nodePos != that.nodePos) {
            return false
        }
        nodeImpl.id == that.nodeImpl.id
    }

    /**
     * @brief Returns hashcode of the node.
     *
     * @return Integer representation of the node's hashcode.
     */
    public func hashCode(): Int64 {
        var df = DefaultHasher()
        df.write(nodePos.beginLine)
        df.write(nodePos.beginColumn)
        df.write(nodePos.endLine)
        df.write(nodePos.endColumn)
        df.write(nodePos.filePath)
        df.write(nodeImpl.id)
        df.finish()
    }

    func transCommentGroupList(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): Array<CommentGroup> {
        let lp = LocalParser(node.children)

        let elements = ArrayList<CommentGroup>()
        while (lp.look(SyntaxNodeKind.CommentGroup)) {
            let cmtGroupNode = lp.consume().getOrThrow()
            elements.add(
                cast<CommentGroup>(
                    SyntaxNodeImplTranslator.translate((cmtGroupNode as NonTerminal).getOrThrow(), startPos + lp.offset,
                        parent)).getOrThrow())
            lp.moveOffset(cmtGroupNode)
        }
        elements.toArray()
    }

    /**
     * @brief The comments array of the SyntaxTreeNode.
     *
     * This array field contains the comments of the node.
     */
    public prop comments: Array<Comment> {
        get() {
            let cmts = ArrayList<Comment>()

            for (propInfo in commentsPropInfo) {
                let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                let stPos = CodePosition(nodePos.beginLine, nodePos.beginColumn,
                    FileInfos(nodePos.fileName, nodePos.filePath))
                let commentGroupList = transCommentGroupList((curNode as NonTerminal).getOrThrow(), stPos + offset, this)
                for (commentGroup in commentGroupList) {
                    cmts.add(all: commentGroup.elements)
                }
            }
            cmts.toArray()
        }
    }
}

/**
 * @brief Enumeration for different at operation kinds.
 *
 * The AtOpKind enumeration represents various types of at operations
 * that can be used in annotation. Each kind is an operator that
 * specifies the type of annotation.
 */
public enum AtOpKind {
    | At
    | AtExcl
    | ...
}

class AnnotationPropInfos {
    let argumentPropInfo: Array<PropInfo>

    init(argumentPropInfo: Array<PropInfo>) {
        this.argumentPropInfo = argumentPropInfo
    }
}

class AnnotationPosInfos {
    let atOpPos: CodePosition
    let lSquarePos: Option<CodePosition>
    let rSquarePos: Option<CodePosition>
    let identifierPos: CodePosition
    let commasPos: Array<CodePosition>

    init(
        atOpPos: CodePosition,
        lSquarePos: Option<CodePosition>,
        rSquarePos: Option<CodePosition>,
        identifierPos: CodePosition,
        commasPos: Array<CodePosition>
    ) {
        this.atOpPos = atOpPos
        this.lSquarePos = lSquarePos
        this.rSquarePos = rSquarePos
        this.identifierPos = identifierPos
        this.commasPos = commasPos
    }
}
/**
 * @brief Class representing an annotation in the syntax tree.
 *
 * This class extends SyntaxTreeNode and contains information about an annotation,
 * including its arguments and identifier.
 */
public class Annotation <: SyntaxTreeNode {
    private let identifier_: String

    private let opKind_: AtOpKind

    private let startPos: CodePosition

    private let propInfos: AnnotationPropInfos

    private let posInfos: AnnotationPosInfos

    /**
     * @brief Get the position of the annotation operator.
     *
     * @return CodePositionRange representing the position of the annotation at operator.
     */
    public func getAtOpPos(): CodePositionRange {
        let endPos = posInfos.atOpPos + match (opKind) {
            case At => SyntaxNodeKind.AtToken.size
            case AtExcl => SyntaxNodeKind.AtExclToken.size
            case _ => 0
        }
        return CodePositionRange(posInfos.atOpPos, endPos)
    }

    /**
     * @brief Get the positions of commas in the code.
     *
     * @return Array<CodePositionRange> containing the positions of commas.
     */
    public func getCommasPos(): Array<CodePositionRange> {
        let commasPosR = ArrayList<CodePositionRange>()
        for (comma in posInfos.commasPos) {
            commasPosR.add(CodePositionRange(comma, comma + SyntaxNodeKind.CommaToken.size))
        }
        return commasPosR.toArray()
    }

    /**
     * @brief Get the position range of the identifier.
     *
     * @return CodePositionRange representing the position range of the identifier.
     */
    public func getIdentifierPos(): CodePositionRange {
        return CodePositionRange(posInfos.identifierPos, posInfos.identifierPos + identifier.size)
    }

    /**
     * @brief Get the position of the left square bracket.
     *
     * @return CodePositionRange representing the position of the left square bracket.
     */
    public func getLSquarePos(): Option<CodePositionRange> {
        if (let Some(v) <- posInfos.lSquarePos) {
            return CodePositionRange(v, v + SyntaxNodeKind.LSquareToken.size)
        }
        return None
    }

    /**
     * @brief Get the position of the right square bracket.
     *
     * @return CodePositionRange representing the position of the right square bracket.
     */
    public func getRSquarePos(): Option<CodePositionRange> {
        if (let Some(v) <- posInfos.rSquarePos) {
            return CodePositionRange(v, v + SyntaxNodeKind.RSquareToken.size)
        }
        return None
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode, identifier_: String,
        opKind_: AtOpKind, posInfos: AnnotationPosInfos, propInfos: AnnotationPropInfos,
        commentsPropInfo: Array<PropInfo>) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, commentsPropInfo)
        this.startPos = startPos
        this.identifier_ = identifier_
        this.opKind_ = opKind_
        this.propInfos = propInfos
        this.posInfos = posInfos
    }

    /**
     * @brief Initialize a new Annotation node.
     */
    public init(arguments: Array<Argument>, identifier: String, opKind: AtOpKind, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createAnnotationImpl(arguments, identifier, opKind, comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<Annotation>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()

        this.startPos = startPos
        this.identifier_ = identifier
        this.opKind_ = opKind
        this.propInfos = redNode.propInfos
        this.posInfos = redNode.posInfos
    }

    /**
     * @brief The arguments of the annotation.
     *
     * This array field contains the arguments of the annotation.
     */
    public prop arguments: Array<Argument> {
        get() {
            let argus = ArrayList<Argument>()
            for (propInfo in propInfos.argumentPropInfo) {
                let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                argus.add(
                    cast<Argument>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this)).getOrThrow())
            }
            argus.toArray()
        }
    }

    /**
     * @brief The identifier of the annotation.
     *
     * This string field holds the identifier of the annotation.
     */
    public prop identifier: String {
        get() {
            identifier_
        }
    }

    public prop opKind: AtOpKind {
        get() {
            opKind_
        }
    }
}

class ArgumentPropInfos {
    let valuePropInfo: PropInfo

    init(valuePropInfo: PropInfo) {
        this.valuePropInfo = valuePropInfo
    }
}

class ArgumentPosInfos {
    let inoutKeyWordPos: Option<CodePosition>
    let identifierPos: Option<CodePosition>
    let colonPos: Option<CodePosition>

    init(inoutKeyWordPos: Option<CodePosition>, identifierPos: Option<CodePosition>, colonPos: Option<CodePosition>) {
        this.inoutKeyWordPos = inoutKeyWordPos
        this.identifierPos = identifierPos
        this.colonPos = colonPos
    }
}

/**
 * @brief Class representing an argument in the syntax tree.
 *
 * This class extends SyntaxTreeNode and contains information about an argument,
 * including its identifier, whether it is in-out, and its value expression.
 */
public class Argument <: SyntaxTreeNode {
    private let identifier_: Option<String>

    private let isInOut_: Bool

    private let isNamed_: Bool

    private let startPos: CodePosition

    private let propInfos: ArgumentPropInfos

    private let posInfos: ArgumentPosInfos

    /**
     * @brief Gets the position range of the 'inout' keyword in the parameter declaration.
     *
     * @return An Option of CodePositionRange representing the position range of the 'inout' keyword, if present.
     */
    public func getInoutKeyWordPos(): Option<CodePositionRange> {
        if (let Some(inoutKeyWordPos) <- posInfos.inoutKeyWordPos) {
            let endPos = inoutKeyWordPos + SyntaxNodeKind.InoutToken.size
            return CodePositionRange(inoutKeyWordPos, endPos)
        }
        return None
    }

    /**
     * @brief Gets the position range of the identifier in the parameter declaration.
     *
     * @return An Option of CodePositionRange representing the position range of the identifier, if present.
     */
    public func getIdentifierPos(): Option<CodePositionRange> {
        if (let Some(identifierPos) <- posInfos.identifierPos && let Some(id) <- identifier) {
            let endPos = identifierPos + id.size
            return CodePositionRange(identifierPos, endPos)
        }
        return None
    }

    /**
     * @brief Gets the position range of the colon in the parameter declaration.
     *
     * @return An Option of CodePositionRange representing the position range of the colon, if present.
     */
    public func getColonPos(): Option<CodePositionRange> {
        if (let Some(colonPos) <- posInfos.colonPos) {
            let endPos = colonPos + SyntaxNodeKind.ColonToken.size
            return CodePositionRange(colonPos, endPos)
        }
        return None
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode, identifier_: Option<String>,
        isInOut_: Bool, isNamed_: Bool, posInfos: ArgumentPosInfos, propInfos: ArgumentPropInfos,
        commentsPropInfo: Array<PropInfo>) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, commentsPropInfo)
        this.identifier_ = identifier_
        this.isInOut_ = isInOut_
        this.isNamed_ = isNamed_
        this.startPos = startPos
        this.posInfos = posInfos
        this.propInfos = propInfos
    }

    /**
     * @brief Initialize a new Argument node.
     */
    public init(identifier: Option<String>, isInOut: Bool, value: Expr, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createArgumentImpl(identifier, isInOut, value, comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<Argument>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()

        this.startPos = startPos
        this.identifier_ = identifier
        this.isInOut_ = isInOut
        this.isNamed_ = redNode.isNamed
        this.propInfos = redNode.propInfos
        this.posInfos = redNode.posInfos
    }

    /**
     * @brief The identifier of the argument.
     *
     * This optional field may contain the identifier of the argument as a string.
     */
    public prop identifier: Option<String> {
        get() {
            identifier_
        }
    }

    /**
     * @brief Indicates if the argument is in-out.
     *
     * This boolean field specifies whether the argument is an in-out argument.
     */
    public prop isInOut: Bool {
        get() {
            isInOut_
        }
    }

    /**
     * @brief Check if the entity is named.
     *
     * This boolean field specifies indicating whether the entity is named.
     */
    public prop isNamed: Bool {
        get() {
            isNamed_
        }
    }

    /**
     * @brief The value expression of the argument.
     *
     * This field contains the expression representing the value of the argument.
     */
    public prop value: Expr {
        get() {
            let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfos.valuePropInfo.index]
            let offset = propInfos.valuePropInfo.offset
            cast<Expr>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this)).getOrThrow()
        }
    }
}

class BlockPropInfos {
    let nodesPropInfo: Option<PropInfo>

    init(nodesPropInfo: Option<PropInfo>) {
        this.nodesPropInfo = nodesPropInfo
    }
}

class BlockPosInfos {
    let lCurlPos: CodePosition
    let rCurlPos: CodePosition

    init(lCurlPos: CodePosition, rCurlPos: CodePosition) {
        this.lCurlPos = lCurlPos
        this.rCurlPos = rCurlPos
    }
}

/**
 * @brief Class representing a block in the syntax tree.
 *
 * This class extends SyntaxTreeNode and contains a collection of child nodes.
 */
public class Block <: SyntaxTreeNode {
    private let startPos: CodePosition

    private let propInfos: BlockPropInfos

    private let posInfos: BlockPosInfos

    /**
     * @brief Get the position of the left curly brace.
     *
     * @return CodePositionRange representing the position of the left curly brace.
     */
    public func getLCurlPos(): CodePositionRange {
        return CodePositionRange(posInfos.lCurlPos, posInfos.lCurlPos + SyntaxNodeKind.LCurlToken.size)
    }

    /**
     * @brief Get the position of the right curly brace.
     *
     * @return CodePositionRange representing the position of the right curly brace.
     */
    public func getRCurlPos(): CodePositionRange {
        return CodePositionRange(posInfos.rCurlPos, posInfos.rCurlPos + SyntaxNodeKind.RCurlToken.size)
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode, posInfos: BlockPosInfos,
        propInfos: BlockPropInfos, commentsPropInfo: Array<PropInfo>) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, commentsPropInfo)
        this.startPos = startPos
        this.posInfos = posInfos
        this.propInfos = propInfos
    }

    /**
     * @brief Initialize a new Block node.
     */
    public init(nodes: Array<SyntaxTreeNode>, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createBlockImpl(nodes, comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<Block>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()

        this.startPos = startPos
        this.propInfos = redNode.propInfos
        this.posInfos = redNode.posInfos
    }

    /**
     * @brief The nodes within the block.
     *
     * This array field contains the child nodes that make up the block.
     */
    public prop nodes: Array<SyntaxTreeNode> {
        get() {
            let nodeList = ArrayList<SyntaxTreeNode>()
            if (let Some(propInfo) <- propInfos.nodesPropInfo) {
                let lp = LocalParser((nodeImpl as NonTerminal).getOrThrow().children, propInfo.offset,
                    index: propInfo.index)
                while (!lp.look(SyntaxNodeKind.RCurlToken)) {
                    var impl = lp.consume().getOrThrow()
                    nodeList.add(
                        cast<SyntaxTreeNode>(SyntaxNodeImplTranslator.translate(impl, startPos + lp.offset, this))
                            .getOrThrow())
                    lp.offset.move(impl.offset)
                }
            }
            nodeList.toArray()
        }
    }
}

class EnumBodyPosInfos {
    let bitOrsPos: Array<CodePosition>
    let ellipsisPos: Option<CodePosition>

    init(bitOrsPos: Array<CodePosition>, ellipsisPos: Option<CodePosition>) {
        this.bitOrsPos = bitOrsPos
        this.ellipsisPos = ellipsisPos
    }
}

class BodyPropInfos {
    let memberDeclsPropInfo: Array<PropInfo>

    init(memberDeclsPropInfo: Array<PropInfo>) {
        this.memberDeclsPropInfo = memberDeclsPropInfo
    }
}

class BodyPosInfos {
    let lCurlPos: CodePosition
    let rCurlPos: CodePosition

    init(lCurlPos: CodePosition, rCurlPos: CodePosition) {
        this.lCurlPos = lCurlPos
        this.rCurlPos = rCurlPos
    }
}

/**
 * @brief Represents the body of a custom decl
 *
 * This class is used to represent the body of a custom declaration.
 * It serves as a container for member declarations.
 */
public class Body <: SyntaxTreeNode {
    private let startPos: CodePosition

    private let propInfos: BodyPropInfos

    private let posInfos: BodyPosInfos

    let enumBodyPosInfos: EnumBodyPosInfos

    /**
     * @brief Get the position range of the left curly brace.
     */
    public func getLCurlPos(): CodePositionRange {
        let endPos = posInfos.lCurlPos + SyntaxNodeKind.LCurlToken.size
        CodePositionRange(posInfos.lCurlPos, endPos)
    }

    /**
     * @brief Get the position range of the right curly brace.
     */
    public func getRCurlPos(): CodePositionRange {
        let endPos = posInfos.rCurlPos + SyntaxNodeKind.RCurlToken.size
        CodePositionRange(posInfos.rCurlPos, endPos)
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode, posInfos: BodyPosInfos,
        enumBodyPosInfos: EnumBodyPosInfos, propInfos: BodyPropInfos, commentsPropInfo: Array<PropInfo>) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, commentsPropInfo)
        this.startPos = startPos
        this.propInfos = propInfos
        this.posInfos = posInfos
        this.enumBodyPosInfos = enumBodyPosInfos
    }

    /**
     * @brief Initialize a new Body node.
     */
    public init(memberDecls: Array<Decl>, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createBodyImpl(memberDecls, comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<Body>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()
        this.startPos = startPos
        this.propInfos = redNode.propInfos
        this.posInfos = redNode.posInfos
        this.enumBodyPosInfos = redNode.enumBodyPosInfos
    }

    /**
     * @brief The array of member declarations in the body.
     *
     * This variable holds an array of member declarations
     */
    public prop memberDecls: Array<Decl> {
        get() {
            let decls = ArrayList<Decl>()
            for (propInfo in propInfos.memberDeclsPropInfo) {
                let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                decls.add(cast<Decl>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this)).getOrThrow())
            }
            decls.toArray()
        }
    }
}

/**
 * @brief Enumeration representing different comment kinds.
 */
public enum CommentKind {
    | Block
    | Document
    | Line
    | ...
}

/**
 * @brief Represents the comment of a SyntaxTreeNode
 *
 * This class is used to represent the comment of a SyntaxTreeNode.
 * It records the comment content and kind.
 */
public class Comment <: SyntaxTreeNode {
    private let startPos: CodePosition

    private let kind_: CommentKind

    private let content_: String

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode, kind_: CommentKind,
        content_: String) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, Array<PropInfo>())
        this.startPos = startPos
        this.kind_ = kind_
        this.content_ = content_
    }

    /**
     * @brief Initialize a new Comment node.
     */
    public init(content: String) {
        super(SyntaxNodeImplCreator.createCommentImpl(content), hasComment: false)
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<Comment>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()
        this.startPos = startPos
        this.kind_ = redNode.kind
        this.content_ = content
    }

    /**
     * @brief The kind of the comment.
     *
     * This variable represents the kind of the comment.
     */
    public prop kind: CommentKind {
        get() {
            kind_
        }
    }

    /**
     * @brief The content of the comment.
     *
     * This variable holds the content of the comment.
     */
    public prop content: String {
        get() {
            content_
        }
    }
}

class CommentGroupPropInfos {
    let elementsPropInfo: Array<PropInfo>

    init(elementsPropInfo: Array<PropInfo>) {
        this.elementsPropInfo = elementsPropInfo
    }
}

class CommentGroup <: SyntaxTreeNode {
    private let startPos: CodePosition

    private let propInfos: CommentGroupPropInfos

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode, propInfos: CommentGroupPropInfos) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, Array<PropInfo>())
        this.startPos = startPos
        this.propInfos = propInfos
    }

    prop elements: Array<Comment> {
        get() {
            let eles = ArrayList<Comment>()
            for (propInfo in propInfos.elementsPropInfo) {
                let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                eles.add(
                    cast<Comment>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, parentNode))
                        .getOrThrow())
            }
            eles.toArray()
        }
    }
}

class GenericConstraintPropInfos {
    let typeArgumentPropInfo: PropInfo
    let upperBoundsPropInfo: Array<PropInfo>

    init(typeArgumentPropInfo: PropInfo, upperBoundsPropInfo: Array<PropInfo>) {
        this.typeArgumentPropInfo = typeArgumentPropInfo
        this.upperBoundsPropInfo = upperBoundsPropInfo
    }
}

class GenericConstraintPosInfos {
    let upperBoundPos: CodePosition
    let bitAndsPos: Array<CodePosition>

    init(upperBoundPos: CodePosition, bitAndsPos: Array<CodePosition>) {
        this.upperBoundPos = upperBoundPos
        this.bitAndsPos = bitAndsPos
    }
}

/**
 * @brief Class representing a generic constraint in the syntax tree.
 *
 * This class extends SyntaxTreeNode and encapsulates a type argument and its upper bounds.
 */
public class GenericConstraint <: SyntaxTreeNode {
    private let startPos: CodePosition

    private let propInfos: GenericConstraintPropInfos

    private let posInfos: GenericConstraintPosInfos
    /**
     * @brief Get the positions of bitwise 'and' operators in the code.
     *
     * @return Array<CodePositionRange> containing the positions of bitwise 'and' operators.
     */
    public func getBitAndsPos(): Array<CodePositionRange> {
        let bitAndPos = ArrayList<CodePositionRange>()
        for (bitAnd in posInfos.bitAndsPos) {
            bitAndPos.add(CodePositionRange(bitAnd, bitAnd + SyntaxNodeKind.BitAndToken.size))
        }
        return bitAndPos.toArray()
    }

    /**
     * @brief Get the position of the upper bound in the code.
     *
     * @return CodePositionRange representing the position of the upper bound.
     */
    public func getUpperBoundPos(): CodePositionRange {
        return CodePositionRange(posInfos.upperBoundPos, posInfos.upperBoundPos + SyntaxNodeKind.UpperBoundToken.size)
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode,
        posInfos: GenericConstraintPosInfos, propInfos: GenericConstraintPropInfos, commentsPropInfo: Array<PropInfo>) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, commentsPropInfo)
        this.startPos = startPos
        this.propInfos = propInfos
        this.posInfos = posInfos
    }

    /**
     * @brief Initialize a new GenericConstraint node.
     */
    public init(typeArgument: TypeAnnotation, upperBounds: Array<TypeAnnotation>, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createGenericConstraintImpl(typeArgument, upperBounds, comments: comments))
        let startPos = DEFAULT_START_POS

        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<GenericConstraint>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()
        this.startPos = startPos
        this.propInfos = redNode.propInfos
        this.posInfos = redNode.posInfos
    }

    /**
     * @brief The type argument of the constraint.
     *
     * This field specifies the type argument of the constraint.
     */
    public prop typeArgument: TypeAnnotation {
        get() {
            let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfos.typeArgumentPropInfo.index]
            let offset = propInfos.typeArgumentPropInfo.offset
            cast<TypeAnnotation>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this)).getOrThrow()
        }
    }

    /**
     * @brief The upper bounds of the type argument.
     *
     * This array field contains the upper bounds that the type argument must satisfy.
     */
    public prop upperBounds: Array<TypeAnnotation> {
        get() {
            let argus = ArrayList<TypeAnnotation>()
            for (propInfo in propInfos.upperBoundsPropInfo) {
                let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                argus.add(
                    cast<TypeAnnotation>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this))
                        .getOrThrow())
            }
            argus.toArray()
        }
    }
}

class GenericConstraintsPropInfos {
    let constraintsPropInfo: Array<PropInfo>

    init(constraintsPropInfo: Array<PropInfo>) {
        this.constraintsPropInfo = constraintsPropInfo
    }
}

class GenericConstraintsPosInfos {
    let whereKeyWordPos: CodePosition
    let commasPos: Array<CodePosition>

    init(whereKeyWordPos: CodePosition, commasPos: Array<CodePosition>) {
        this.whereKeyWordPos = whereKeyWordPos
        this.commasPos = commasPos
    }
}

/**
 * @brief Class representing multiple generic constraints in the syntax tree.
 *
 * This class extends SyntaxTreeNode and contains a collection of GenericConstraint instances.
 */
public class GenericConstraints <: SyntaxTreeNode {
    private let startPos: CodePosition

    private let propInfos: GenericConstraintsPropInfos

    private let posInfos: GenericConstraintsPosInfos
    /**
     * @brief Get the positions of commas in the code.
     *
     * @return Array<CodePositionRange> containing the positions of commas.
     */
    public func getCommasPos(): Array<CodePositionRange> {
        let commasPos = ArrayList<CodePositionRange>()
        for (comma in posInfos.commasPos) {
            commasPos.add(CodePositionRange(comma, comma + SyntaxNodeKind.CommaToken.size))
        }
        return commasPos.toArray()
    }

    /**
     * @brief Get the position of the 'where' keyword.
     *
     * @return CodePositionRange representing the position of the 'where' keyword.
     */
    public func getWhereKeyWordPos(): CodePositionRange {
        return CodePositionRange(posInfos.whereKeyWordPos, posInfos.whereKeyWordPos + SyntaxNodeKind.WhereToken.size)
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode,
        posInfos: GenericConstraintsPosInfos, propInfos: GenericConstraintsPropInfos, commentsPropInfo: Array<PropInfo>) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, commentsPropInfo)
        this.startPos = startPos
        this.propInfos = propInfos
        this.posInfos = posInfos
    }

    /**
     * @brief Initialize a new GenericConstraints node.
     */
    public init(constraints: Array<GenericConstraint>, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createGenericConstraintsImpl(constraints, comments: comments))
        let startPos = DEFAULT_START_POS

        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<GenericConstraints>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()
        this.startPos = startPos
        this.propInfos = redNode.propInfos
        this.posInfos = redNode.posInfos
    }

    /**
     * @brief The collection of generic constraints.
     *
     * This array field contains multiple GenericConstraint instances.
     */
    public prop constraints: Array<GenericConstraint> {
        get() {
            let cons = ArrayList<GenericConstraint>()
            for (propInfo in propInfos.constraintsPropInfo) {
                let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                cons.add(
                    cast<GenericConstraint>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this))
                        .getOrThrow())
            }
            cons.toArray()
        }
    }
}

/**
 * @brief Enumeration representing different modifier kinds.
 */
@Derive[Equatable]
public enum ModifierKind {
    | Abstract
    | Internal
    | Mut
    | Open
    | Operator
    | Override
    | Private
    | Protected
    | Public
    | Redef
    | Sealed
    | Static
    | Unsafe
    | Const
    | ...
}

/**
 * @brief Class representing a modifier in the syntax tree.
 *
 * This class extends SyntaxTreeNode and encapsulates a ModifierKind.
 */
public class Modifier <: SyntaxTreeNode {
    private let kind_: ModifierKind

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode, kind_: ModifierKind,
        commentsPropInfo: Array<PropInfo>) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, commentsPropInfo)
        this.kind_ = kind_
    }

    /**
     * @brief Initialize a new Modifier node.
     */
    public init(kind: ModifierKind, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createModifierImpl(kind, comments: comments))
        this.kind_ = kind
    }

    /**
     * @brief The kind of modifier.
     *
     * This field specifies the kind of modifier this instance represents.
     */
    public prop kind: ModifierKind {
        get() {
            kind_
        }
    }
}

class ParameterListPropInfos {
    let paramsPropInfo: Array<PropInfo>

    init(paramsPropInfo: Array<PropInfo>) {
        this.paramsPropInfo = paramsPropInfo
    }
}

class ParameterListPosInfos {
    let paramsLParenPos: Option<CodePosition>
    let paramsCommasPos: Array<CodePosition>
    let paramsRParenPos: Option<CodePosition>

    init(paramsLParenPos: Option<CodePosition>, paramsCommasPos: Array<CodePosition>,
        paramsRParenPos: Option<CodePosition>) {
        this.paramsLParenPos = paramsLParenPos
        this.paramsCommasPos = paramsCommasPos
        this.paramsRParenPos = paramsRParenPos
    }
}

public class ParameterList <: SyntaxTreeNode {
    private let startPos: CodePosition

    private let propInfos: ParameterListPropInfos

    private let posInfos: ParameterListPosInfos

    /**
     * @brief Get the position ranges of commas in the parameter list.
     */
    public func getParamsCommasPos(): Array<CodePositionRange> {
        let ret = ArrayList<CodePositionRange>()
        for (i in 0..posInfos.paramsCommasPos.size) {
            let endPos = posInfos.paramsCommasPos[i] + SyntaxNodeKind.CommaToken.size
            ret.add(CodePositionRange(posInfos.paramsCommasPos[i], endPos))
        }
        ret.toArray()
    }

    /**
     * @brief Get the position range of the left parenthesis in the parameter list.
     */
    public func getParamsLParenPos(): Option<CodePositionRange> {
        if (let Some(v) <- posInfos.paramsLParenPos) {
            let endPos = v + SyntaxNodeKind.LParenToken.size
            return CodePositionRange(v, endPos)
        }
        None
    }

    /**
     * @brief Get the position range of the right parenthesis in the parameter list.
     */
    public func getParamsRParenPos(): Option<CodePositionRange> {
        if (let Some(v) <- posInfos.paramsRParenPos) {
            let endPos = v + SyntaxNodeKind.RParenToken.size
            return CodePositionRange(v, endPos)
        }
        None
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode, posInfos: ParameterListPosInfos,
        propInfos: ParameterListPropInfos, commentsPropInfo: Array<PropInfo>) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, commentsPropInfo)
        this.startPos = startPos
        this.posInfos = posInfos
        this.propInfos = propInfos
    }

    /**
     * @brief Initialize node with parameters.
     */
    public init(parameters: Array<Parameter>, hasParen!: Bool = true, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createParameterListImpl(parameters, hasParen: hasParen, comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<ParameterList>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()
        this.startPos = startPos
        this.propInfos = redNode.propInfos
        this.posInfos = redNode.posInfos
    }

    /**
     * @brief Get the params in the parameter list.
     */
    public prop params: Array<Parameter> {
        get() {
            let params = ArrayList<Parameter>()
            for (propInfo in propInfos.paramsPropInfo) {
                var curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                var offset = propInfo.offset
                var currentPos = startPos + offset
                params.add(
                    cast<Parameter>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this)).getOrThrow())
            }
            params.toArray()
        }
    }
}

class SourceFilePropInfos {
    let importListsPropInfo: Array<PropInfo>

    let pkgHeaderPropInfo: Option<PropInfo>

    let topLevelDeclsPropInfo: Array<PropInfo>

    let ftrDirectivePropInfo: Option<PropInfo>

    init(importListsPropInfo: Array<PropInfo>, pkgHeaderPropInfo: Option<PropInfo>,
        topLevelDeclsPropInfo: Array<PropInfo>, ftrDirectivePropInfo: Option<PropInfo>) {
        this.importListsPropInfo = importListsPropInfo
        this.pkgHeaderPropInfo = pkgHeaderPropInfo
        this.topLevelDeclsPropInfo = topLevelDeclsPropInfo
        this.ftrDirectivePropInfo = ftrDirectivePropInfo
    }
}

/**
 * @brief Class representing a source file in the syntax tree.
 *
 * This class extends SyntaxTreeNode and contains information about a source file,
 * including its name, path, package header, import lists, and top-level declarations.
 */
public class SourceFile <: SyntaxTreeNode {
    private let name_: String

    private let path_: String

    private let startPos: CodePosition

    private let propInfos: SourceFilePropInfos

    prop isMacroPackage: Bool {
        get() {
            pkgHeader?.isMacroPkg ?? false
        }
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode, name_: String, path_: String,
        propInfos: SourceFilePropInfos, commentsPropInfo: Array<PropInfo>) {
        super(CodePositionRange(startPos, startPos + nodeImpl.offset), nodeImpl, parentNode, commentsPropInfo)
        this.name_ = name_
        this.path_ = path_
        this.startPos = startPos
        this.propInfos = propInfos
    }

    /**
     * @brief Initialize node with import list, file name, file path, package header, top level declrations and features directive.
     */
    public init(importLists: Array<ImportList>, name: String, path: String, pkgHeader: Option<PackageHeader>,
        topLevelDecls: Array<Decl>, ftrDirective!: Option<FeaturesDirective> = None, comments!: Array<Comment> = []) {
        super(
            SyntaxNodeImplCreator.createSourceFileImpl(importLists, pkgHeader, topLevelDecls, ftrDirective: ftrDirective,
                comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<SourceFile>(SyntaxNodeImplTranslator.translateFile(curNode, path, parent: None)).getOrThrow()
        this.name_ = name
        this.path_ = path
        this.startPos = startPos
        this.propInfos = redNode.propInfos
    }

    /**
     * @brief The import lists of the source file.
     *
     * This array field contains the import lists of the source file.
     */
    public prop importLists: Array<ImportList> {
        get() {
            let imports = ArrayList<ImportList>()
            for (propInfo in propInfos.importListsPropInfo) {
                let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                imports.add(
                    cast<ImportList>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this)).getOrThrow())
            }
            imports.toArray()
        }
    }

    /**
     * @brief The name of the source file.
     *
     * This string field holds the name of the source file.
     */
    public prop name: String {
        get() {
            name_
        }
    }

    /**
     * @brief The path of the source file.
     *
     * This string field holds the path of the source file.
     */
    public prop path: String {
        get() {
            path_
        }
    }

    /**
     * @brief The package header of the source file.
     *
     * This optional field may contain the package header of the source file.
     */
    public prop pkgHeader: Option<PackageHeader> {
        get() {
            if (let Some(propInfo) <- propInfos.pkgHeaderPropInfo) {
                let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                cast<PackageHeader>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this)).getOrThrow()
            } else {
                None
            }
        }
    }

    /**
     * @brief The top-level declarations in the source file.
     *
     * This array field contains the top-level declarations in the source file.
     */
    public prop topLevelDecls: Array<Decl> {
        get() {
            let decls = ArrayList<Decl>()
            for (propInfo in propInfos.topLevelDeclsPropInfo) {
                let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                decls.add(cast<Decl>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this)).getOrThrow())
            }
            decls.toArray()
        }
    }

    /**
     * @brief The features directive of the source file.
     *
     * This optional field may contain the features directive of the source file.
     */
    public prop ftrDirective: Option<FeaturesDirective> {
        get() {
            if (let Some(propInfo) <- propInfos.ftrDirectivePropInfo) {
                let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                cast<FeaturesDirective>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this))
                    .getOrThrow()
            } else {
                None
            }
        }
    }
}