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

private func createMacroOptionalTokenRange(pos: Option<CodePosition>, size: Int64): Option<CodePositionRange> {
    if (let Some(v) <- pos) {
        return CodePositionRange(v, v + size)
    }
    return None
}

private func translateMacroChild<T>(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode,
    propInfo: PropInfo): T where T <: SyntaxTreeNode {
    let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
    return cast<T>(SyntaxNodeImplTranslator.translate(curNode, startPos + propInfo.offset, parentNode)).getOrThrow()
}

private func translateMacroTokenList(nodeImpl: SyntaxNodeImpl, parentNode: SyntaxTreeNode, propInfo: PropInfo): Tokens {
    let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
    return parentNode.transTokenList(curNode)
}

private func translateMacroInputs(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: SyntaxTreeNode,
    propInfo: PropInfo, lParenPos: Option<CodePositionRange>): MacroExpandInput {
    let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
    let offset = propInfo.offset

    if (lParenPos.isSome()) {
        return WithParens(parentNode.transTokenList(curNode))
    }
    return WithoutParens(
        cast<Decl>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, parentNode)).getOrThrow())
}

class MacroExpandDeclPropInfos {
    let calleeMacroPropInfo: PropInfo
    let macroAttrsPropInfo: PropInfo
    let macroInputsPropInfo: PropInfo

    init(calleeMacroPropInfo: PropInfo, macroAttrsPropInfo: PropInfo, macroInputsPropInfo: PropInfo) {
        this.calleeMacroPropInfo = calleeMacroPropInfo
        this.macroAttrsPropInfo = macroAttrsPropInfo
        this.macroInputsPropInfo = macroInputsPropInfo
    }
}

class MacroExpandDeclPosInfos {
    let atPos: CodePosition
    let lSquarePos: Option<CodePosition>
    let rSquarePos: Option<CodePosition>
    let lParenPos: Option<CodePosition>
    let rParenPos: Option<CodePosition>

    init(atPos: CodePosition, lSquarePos: Option<CodePosition>, rSquarePos: Option<CodePosition>,
        lParenPos: Option<CodePosition>, rParenPos: Option<CodePosition>) {
        this.atPos = atPos
        this.lSquarePos = lSquarePos
        this.rSquarePos = rSquarePos
        this.lParenPos = lParenPos
        this.rParenPos = rParenPos
    }
}

/**
 * @brief Represents a macro expansion declaration, which extends from Decl.
 */
public class MacroExpandDecl <: Decl {
    private let startPos: CodePosition

    private let propInfos: MacroExpandDeclPropInfos

    private let posInfos: MacroExpandDeclPosInfos

    /**
     * @brief Get the position range of the 'at' token.
     */
    public func getAtPos(): CodePositionRange {
        return CodePositionRange(posInfos.atPos, posInfos.atPos + SyntaxNodeKind.AtToken.size)
    }

    /**
     * @brief Get the position range of the left square bracket if present.
     */
    public func getLSquarePos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.lSquarePos, SyntaxNodeKind.LSquareToken.size)
    }

    /**
     * @brief Get the position range of the right square bracket if present.
     */
    public func getRSquarePos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.rSquarePos, SyntaxNodeKind.RSquareToken.size)
    }

    /**
     * @brief Get the position range of the left parenthesis if present.
     */
    public func getLParenPos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.lParenPos, SyntaxNodeKind.LParenToken.size)
    }

    /**
     * @brief Get the position range of the right parenthesis if present.
     */
    public func getRParenPos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.rParenPos, SyntaxNodeKind.RParenToken.size)
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode,
        posInfos: MacroExpandDeclPosInfos, propInfos: MacroExpandDeclPropInfos, 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 MacroExpandDecl node.
     */
    public init(calleeMacro: Expr, macroAttrs: Tokens, macroInputs: MacroExpandInput,
        annotations!: Array<Annotation> = [], comments!: Array<Comment> = []) {
        super(
            SyntaxNodeImplCreator.createMacroExpandDeclImpl(calleeMacro, macroAttrs, macroInputs,
                annotations: annotations, comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<MacroExpandDecl>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()

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

    /**
     * @brief The expression of the macro being called.
     */
    public prop calleeMacro: Expr {
        get() {
            return translateMacroChild<Expr>(nodeImpl, startPos, this, propInfos.calleeMacroPropInfo)
        }
    }

    /**
     * @brief The attributes of the macro.
     */
    public prop macroAttrs: Tokens {
        get() {
            return translateMacroTokenList(nodeImpl, this, propInfos.macroAttrsPropInfo)
        }
    }

    /**
     * @brief The input declaration or tokens to the macro.
     */
    public prop macroInputs: MacroExpandInput {
        get() {
            return translateMacroInputs(nodeImpl, startPos, this, propInfos.macroInputsPropInfo, getLParenPos())
        }
    }
}

class MacroExpandExprPropInfos {
    let calleeMacroPropInfo: PropInfo
    let macroAttrsPropInfo: PropInfo
    let macroInputsPropInfo: PropInfo

    init(calleeMacroPropInfo: PropInfo, macroAttrsPropInfo: PropInfo, macroInputsPropInfo: PropInfo) {
        this.calleeMacroPropInfo = calleeMacroPropInfo
        this.macroAttrsPropInfo = macroAttrsPropInfo
        this.macroInputsPropInfo = macroInputsPropInfo
    }
}

class MacroExpandExprPosInfos {
    let atPos: CodePosition
    let lSquarePos: Option<CodePosition>
    let rSquarePos: Option<CodePosition>
    let lParenPos: Option<CodePosition>
    let rParenPos: Option<CodePosition>

    init(atPos: CodePosition, lSquarePos: Option<CodePosition>, rSquarePos: Option<CodePosition>,
        lParenPos: Option<CodePosition>, rParenPos: Option<CodePosition>) {
        this.atPos = atPos
        this.lSquarePos = lSquarePos
        this.rSquarePos = rSquarePos
        this.lParenPos = lParenPos
        this.rParenPos = rParenPos
    }
}

/**
 * @brief Represents a macro expansion expression, which extends from Expr.
 */
public class MacroExpandExpr <: Expr {
    private let startPos: CodePosition

    private let propInfos: MacroExpandExprPropInfos

    private let posInfos: MacroExpandExprPosInfos

    /**
     * @brief Get the position range of the 'at' token.
     */
    public func getAtPos(): CodePositionRange {
        return CodePositionRange(posInfos.atPos, posInfos.atPos + SyntaxNodeKind.AtToken.size)
    }

    /**
     * @brief Get the position range of the left square bracket if present.
     */
    public func getLSquarePos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.lSquarePos, SyntaxNodeKind.LSquareToken.size)
    }

    /**
     * @brief Get the position range of the right square bracket if present.
     */
    public func getRSquarePos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.rSquarePos, SyntaxNodeKind.RSquareToken.size)
    }

    /**
     * @brief Get the position range of the left parenthesis if present.
     */
    public func getLParenPos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.lParenPos, SyntaxNodeKind.LParenToken.size)
    }

    /**
     * @brief Get the position range of the right parenthesis if present.
     */
    public func getRParenPos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.rParenPos, SyntaxNodeKind.RParenToken.size)
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode,
        posInfos: MacroExpandExprPosInfos, propInfos: MacroExpandExprPropInfos, 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 callee, attributes and inputs.
     */
    public init(calleeMacro: Expr, macroAttrs: Tokens, macroInputs: MacroExpandInput, comments!: Array<Comment> = []) {
        super(
            unsafe { SyntaxNodeImplCreator.createMacroExpandExprImpl(calleeMacro, macroAttrs, macroInputs,
                comments: comments) })
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<MacroExpandExpr>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()
        this.startPos = startPos
        this.propInfos = redNode.propInfos
        this.posInfos = redNode.posInfos
    }

    /**
     * @brief The expression of the macro being called.
     */
    public prop calleeMacro: Expr {
        get() {
            return translateMacroChild<Expr>(nodeImpl, startPos, this, propInfos.calleeMacroPropInfo)
        }
    }

    /**
     * @brief The attributes of the macro.
     */
    public prop macroAttrs: Tokens {
        get() {
            return translateMacroTokenList(nodeImpl, this, propInfos.macroAttrsPropInfo)
        }
    }

    /**
     * @brief The input declaration or tokens to the macro.
     */
    public prop macroInputs: MacroExpandInput {
        get() {
            return translateMacroInputs(nodeImpl, startPos, this, propInfos.macroInputsPropInfo, getLParenPos())
        }
    }
}

/**
 * @brief Enumerates the possible input forms for macro expansions.
 */
/**
 * @brief Enumerates the possible input forms for macro expansions.
 */
public enum MacroExpandInput {
    | WithoutParens(Decl)
    | WithParens(Tokens)
    | ...
}

class MacroExpandParamPropInfos {
    let calleeMacroPropInfo: PropInfo
    let macroAttrsPropInfo: PropInfo
    let macroInputsPropInfo: PropInfo

    init(calleeMacroPropInfo: PropInfo, macroAttrsPropInfo: PropInfo, macroInputsPropInfo: PropInfo) {
        this.calleeMacroPropInfo = calleeMacroPropInfo
        this.macroAttrsPropInfo = macroAttrsPropInfo
        this.macroInputsPropInfo = macroInputsPropInfo
    }
}

class MacroExpandParamPosInfos {
    let atPos: CodePosition
    let lSquarePos: Option<CodePosition>
    let rSquarePos: Option<CodePosition>
    let lParenPos: Option<CodePosition>
    let rParenPos: Option<CodePosition>

    init(atPos: CodePosition, lSquarePos: Option<CodePosition>, rSquarePos: Option<CodePosition>,
        lParenPos: Option<CodePosition>, rParenPos: Option<CodePosition>) {
        this.atPos = atPos
        this.lSquarePos = lSquarePos
        this.rSquarePos = rSquarePos
        this.lParenPos = lParenPos
        this.rParenPos = rParenPos
    }
}

/**
 * @brief Represents a function parameter that involves macro expansion.
 *        Used for parameters that are processed by macros during compilation.
 */
/**
 * @brief Represents a function parameter that involves macro expansion.
 *        Used for parameters that are processed by macros during compilation.
 */
public class MacroExpandParam <: Parameter {
    private let startPos: CodePosition

    private let propInfos: MacroExpandParamPropInfos

    private let posInfos: MacroExpandParamPosInfos

    /**
     * @brief Get the position range of the 'at' token.
     */
    public func getAtPos(): CodePositionRange {
        return CodePositionRange(posInfos.atPos, posInfos.atPos + SyntaxNodeKind.AtToken.size)
    }

    /**
     * @brief Get the position range of the left square bracket if present.
     */
    public func getLSquarePos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.lSquarePos, SyntaxNodeKind.LSquareToken.size)
    }

    /**
     * @brief Get the position range of the right square bracket if present.
     */
    public func getRSquarePos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.rSquarePos, SyntaxNodeKind.RSquareToken.size)
    }

    /**
     * @brief Get the position range of the left parenthesis if present.
     */
    public func getLParenPos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.lParenPos, SyntaxNodeKind.LParenToken.size)
    }

    /**
     * @brief Get the position range of the right parenthesis if present.
     */
    public func getRParenPos(): Option<CodePositionRange> {
        return createMacroOptionalTokenRange(posInfos.rParenPos, SyntaxNodeKind.RParenToken.size)
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode,
        posInfos: MacroExpandParamPosInfos, propInfos: MacroExpandParamPropInfos, 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 MacroExpandParam node.
     */
    public init(calleeMacro: Expr, macroAttrs: Tokens, macroInputs: MacroExpandInput,
        annotations!: Array<Annotation> = [], comments!: Array<Comment> = []) {
        super(
            SyntaxNodeImplCreator.createMacroExpandParamImpl(calleeMacro, macroAttrs, macroInputs,
                annotations: annotations, comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<MacroExpandParam>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()

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

    /**
     * @brief The expression of the macro being called.
     */
    public prop calleeMacro: Expr {
        get() {
            return translateMacroChild<Expr>(nodeImpl, startPos, this, propInfos.calleeMacroPropInfo)
        }
    }

    /**
     * @brief The attributes of the macro.
     */
    public prop macroAttrs: Tokens {
        get() {
            return translateMacroTokenList(nodeImpl, this, propInfos.macroAttrsPropInfo)
        }
    }

    /**
     * @brief The input declaration or tokens to the macro.
     */
    public prop macroInputs: MacroExpandInput {
        get() {
            return translateMacroInputs(nodeImpl, startPos, this, propInfos.macroInputsPropInfo, getLParenPos())
        }
    }
}

class QuoteInterpolationExprPosInfos {
    let dollarPos: CodePosition
    let lParenPos: Option<CodePosition>
    let rParenPos: Option<CodePosition>

    init(dollarPos: CodePosition, lParenPos: Option<CodePosition>, rParenPos: Option<CodePosition>) {
        this.dollarPos = dollarPos
        this.lParenPos = lParenPos
        this.rParenPos = rParenPos
    }
}

class QuoteInterpolationExprPropInfos {
    let exprPropInfo: PropInfo

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

/**
 * @brief Represents a quote interpolation expression, which extends from Expr, e.g. $(A.a) or $a.
 */
public class QuoteInterpolationExpr <: Expr {
    private let startPos: CodePosition

    private let propInfos: QuoteInterpolationExprPropInfos

    private let posInfos: QuoteInterpolationExprPosInfos

    /**
     * @brief Gets the position range of the dollar sign in the code.
     *
     * @return A CodePositionRange representing the position range of the dollar sign.
     */
    public func getDollarPos(): CodePositionRange {
        let endPos = posInfos.dollarPos + SyntaxNodeKind.DollarToken.size
        return CodePositionRange(posInfos.dollarPos, endPos)
    }

    /**
     * @brief Gets the position range of the left parenthesis in the code.
     *
     * @return A CodePositionRange representing the position range of the left parenthesis.
     */
    public func getLParenPos(): Option<CodePositionRange> {
        if (let Some(lParenPos) <- posInfos.lParenPos) {
            let endPos = lParenPos + SyntaxNodeKind.LParenToken.size
            return CodePositionRange(lParenPos, endPos)
        } else {
            return None
        }
    }

    /**
     * @brief Gets the position range of the right parenthesis in the code.
     *
     * @return A CodePositionRange representing the position range of the right parenthesis.
     */
    public func getRParenPos(): Option<CodePositionRange> {
        if (let Some(rParenPos) <- posInfos.rParenPos) {
            let endPos = rParenPos + SyntaxNodeKind.RParenToken.size
            return CodePositionRange(rParenPos, endPos)
        } else {
            return None
        }
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode,
        posInfos: QuoteInterpolationExprPosInfos, propInfos: QuoteInterpolationExprPropInfos,
        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 quoted expr.
     */
    public init(expr: Expr, hasParen!: Bool = false, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createQuoteInterpolationExprImpl(expr, hasParen: hasParen, comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<QuoteInterpolationExpr>(SyntaxNodeImplTranslator.translate(curNode, startPos, None))
            .getOrThrow()
        this.startPos = startPos
        this.propInfos = redNode.propInfos
        this.posInfos = redNode.posInfos
    }

    /**
     * @brief The expression to be interpolated.
     */
    public prop expr: Expr {
        get() {
            let curNode = (nodeImpl as NonTerminal).getOrThrow().children[propInfos.exprPropInfo.index]
            let offset = propInfos.exprPropInfo.offset
            cast<Expr>(SyntaxNodeImplTranslator.translate(curNode, startPos + offset, this)).getOrThrow()
        }
    }
}

class QuoteTokenPropInfos {
    let contentPropInfo: PropInfo

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

/**
 * @brief Represents a quote token, which extends from SyntaxTreeNode.
 */
public class QuoteToken <: SyntaxTreeNode {
    private let startPos: CodePosition

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

    /**
     * @brief Initialize node with quoted tokens.
     */
    public init(content: Tokens, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createQuoteTokenImpl(content, comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<QuoteToken>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()
        this.startPos = startPos
    }

    /**
     * @brief The content of the quote token.
     */
    public prop content: Tokens {
        get() {
            let content = ArrayList<Token>()
            for (child in (nodeImpl as NonTerminal).getOrThrow().children) {
                match (child.kind) {
                    case SyntaxNodeKind.Token =>
                        if (let Some(tk) <- (child as TokenTerminal)) {
                            content.add(Token(tk.tokenKind, tk.value))
                        }
                    case _ => ()
                }
            }
            Tokens(content)
        }
    }
}

class QuoteExprPosInfos {
    let quoteKeyWordPos: CodePosition
    let lParenPos: CodePosition
    let rParenPos: CodePosition

    init(quoteKeyWordPos: CodePosition, lParenPos: CodePosition, rParenPos: CodePosition) {
        this.quoteKeyWordPos = quoteKeyWordPos
        this.lParenPos = lParenPos
        this.rParenPos = rParenPos
    }
}

class QuoteExprPropInfos {
    let tokensOrRefExprPropInfo: Array<PropInfo>

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

/**
 * @brief Represents a quote expression, which extends from Expr.
 * quote(
 *     arr = $(intList)
 *     x = $(float)
 * )
 */
public class QuoteExpr <: Expr {
    private let startPos: CodePosition

    private let propInfos: QuoteExprPropInfos

    private let posInfos: QuoteExprPosInfos

    /**
     * @brief Gets the position range of the 'quote' keyword in the code.
     *
     * @return A CodePositionRange representing the position range of the 'quote' keyword.
     */
    public func getQuoteKeyWordPos(): CodePositionRange {
        let endPos = posInfos.quoteKeyWordPos + SyntaxNodeKind.QuoteToken.size
        return CodePositionRange(posInfos.quoteKeyWordPos, endPos)
    }
    /**
     * @brief Gets the position range of the left parenthesis in the code.
     *
     * @return A CodePositionRange representing the position range of the left parenthesis.
     */
    public func getLParenPos(): CodePositionRange {
        let endPos = posInfos.lParenPos + SyntaxNodeKind.LParenToken.size
        return CodePositionRange(posInfos.lParenPos, endPos)
    }

    /**
     * @brief Gets the position range of the right parenthesis in the code.
     *
     * @return A CodePositionRange representing the position range of the right parenthesis.
     */
    public func getRParenPos(): CodePositionRange {
        let endPos = posInfos.rParenPos + SyntaxNodeKind.RParenToken.size
        return CodePositionRange(posInfos.rParenPos, endPos)
    }

    init(nodeImpl: SyntaxNodeImpl, startPos: CodePosition, parentNode: ?SyntaxTreeNode, posInfos: QuoteExprPosInfos,
        propInfos: QuoteExprPropInfos, 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 array of quoted content.
     */
    public init(tokensOrRefExpr: Array<QuoteExprContent>, comments!: Array<Comment> = []) {
        super(SyntaxNodeImplCreator.createQuoteExprImpl(tokensOrRefExpr, comments: comments))
        let startPos = DEFAULT_START_POS
        let curNode = (this.nodeImpl as NonTerminal).getOrThrow()
        let redNode = cast<QuoteExpr>(SyntaxNodeImplTranslator.translate(curNode, startPos, None)).getOrThrow()
        this.posInfos = redNode.posInfos
        this.propInfos = redNode.propInfos
        this.startPos = startPos
    }

    /**
     * @brief An array containing the tokens or reference expressions.
     */
    public prop tokensOrRefExpr: Array<QuoteExprContent> {
        get() {
            let tokensOrRefExprs = ArrayList<QuoteExprContent>()

            for (propInfo in propInfos.tokensOrRefExprPropInfo) {
                let node = (nodeImpl as NonTerminal).getOrThrow().children[propInfo.index]
                let offset = propInfo.offset
                match (node.kind) {
                    case SyntaxNodeKind.QuoteTokenExpr => tokensOrRefExprs.add(
                        TokenPart(
                            cast<QuoteToken>(SyntaxNodeImplTranslator.translate(node, startPos + offset, this))
                                .getOrThrow()))
                    case _ => tokensOrRefExprs.add(
                        QuoteInterpolation(
                            cast<QuoteInterpolationExpr>(SyntaxNodeImplTranslator.translate(node, startPos + offset,
                                this)).getOrThrow()))
                }
            }

            tokensOrRefExprs.toArray()
        }
    }
}

/**
 * @brief Represents the content of a quote expression.
 */
public enum QuoteExprContent {
    | TokenPart(QuoteToken)
    | QuoteInterpolation(QuoteInterpolationExpr)
    | ...
}