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

// The Cangjie API is in Beta. For details on its capabilities and limitations, please refer to the README file.

package std.ast

public import std.collection.ArrayList

abstract sealed class Node <: ToTokens {
    init() {}
    protected var begin_: Position = Position()
    protected var end_: Position = Position()
    protected var astKind: String = "node"
    protected var currentIndent: UInt16 = 0
    protected var beginNode: Bool = false
    public mut prop beginPos: Position {
        get() {
            begin_
        }
        set(v) {
            begin_ = v
        }
    }
    public mut prop endPos: Position {
        get() {
            end_
        }
        set(v) {
            end_ = v
        }
    }
    public func toTokens(): Tokens

    public open func traverse(v: Visitor): Unit {
        v.visit(this)
    }

    public func dump(): Unit {
        beginNode = true
        print(dump(0))
    }

    protected func dump(indent: UInt16): String
}

public class Annotation <: Node {
    init(at: Token, identifier: Token, lSquare: Token, args: ArrayList<Argument>, rSquare: Token, attrs: Tokens,
        cond: Option<Expr>) {
        super()
        at_ = at
        identifier_ = identifier
        lSquare_ = lSquare
        args_ = args
        rSquare_ = rSquare
        attrs_ = attrs
        cond_ = cond
    }
    public init(inputs: Tokens) {
        super()
        try {
            var anno = parseDecl(inputs.append(Token(NL)).append(innerToken_)).annotations[0]
            this.begin_ = anno.beginPos
            this.end_ = anno.endPos
            at_ = anno.at_
            lSquare_ = anno.lSquare_
            identifier_ = anno.identifier_
            rSquare_ = anno.rSquare_
            attrs_ = anno.attrs_
            cond_ = anno.cond_
            args_ = anno.args_
        } catch (e: ParseASTException | IndexOutOfBoundsException) {
            throw ASTException("Cannot construct the 'Annotation' node.")
        }
    }
    public init() {}
    private var innerToken_: Tokens = quote(func innerToken() {})
    private var at_: Token = Token(TokenKind.AT)
    private var identifier_: Token = Token(TokenKind.IDENTIFIER, "")
    // Common annotation
    private var lSquare_: Token = Token(TokenKind.LSQUARE)
    private var args_: ArrayList<Argument> = ArrayList<Argument>()
    private var rSquare_: Token = Token(TokenKind.RSQUARE)
    // Attribute annotation
    private var attrs_: Tokens = Tokens()
    // Condtional Compile
    private var cond_: Option<Expr> = None<Expr>

    public mut prop at: Token {
        get() {
            at_
        }
        set(v) {
            if (v.kind != TokenKind.AT && v.kind != TokenKind.AT_EXCL) {
                throw ASTException("Illegal TokenKind, TokenKind should be ${AT} or ${AT_EXCL}")
            }
            at_ = v
        }
    }
    public mut prop identifier: Token {
        get() {
            identifier_
        }
        set(v) {
            identifier_ = v
        }
    }
    public mut prop arguments: ArrayList<Argument> {
        get() {
            args_
        }
        set(v) {
            args_ = v
        }
    }
    public mut prop attributes: Tokens {
        get() {
            attrs_
        }
        set(v) {
            attrs_ = v
        }
    }
    public mut prop condition: Expr {
        get() {
            match (cond_) {
                case Some(v) => v
                case None => throw ASTException("Current Annotation node is not `When` annotation")
            }
        }
        set(v) {
            cond_ = Some(v)
        }
    }

    public func toTokens(): Tokens {
        var ret = Tokens()
        ret.append(at_).append(identifier_)
        match (identifier_.value) {
            case "When" => ret
                .append(lSquare_)
                .append(cond_.getOrThrow().toTokens())
                .append(rSquare_)
                .append(Token(NL))
            case "Attribute" =>
                if (attrs_.size == 0) {
                    ret.append(Token(NL))
                } else {
                    ret.append(lSquare_)
                    for (i in 0..attrs_.size - 1) {
                        ret.append(attrs_[i]).append(Token(TokenKind.COMMA))
                    }
                    ret.append(attrs_[attrs_.size - 1]).append(rSquare_).append(Token(NL))
                }
            case _ =>
                if (!args_.isEmpty()) {
                    ret.append(lSquare_)
                    ret.append(args_.toTokens())
                    ret.append(rSquare_)
                }
                ret.append(Token(NL))
        }
        return ret
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        match (this.cond_) {
            case Some(node) => node.traverse(v)
            case _ => ()
        }
        return
    }

    protected func dump(indent: UInt16): String {
        var ret: String = "Annotation {\n"
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        var childIndent = getIndent(currentIndent)
        ret += childIndent + "-identifier: ${identifier_.value}\n"
        for (i in 0..args_.size) {
            ret += childIndent + "-arguments: ${i}, "
            ret += args_[i].dump(currentIndent)
        }
        for (i in 0..attrs_.size) {
            ret += childIndent + "-attributes: ${i}, "
            ret += getTokenIndent("${attrs_[i].value}", attrs_[i], currentIndent)
        }
        match (cond_) {
            case Some(v) =>
                ret += childIndent + "-condition: "
                ret += v.dump(currentIndent)
            case None => ()
        }
        ret += getIndent(indent) + "}\n"
        ret
    }
}

public class Modifier <: Node {
    public init(keyword: Token) {
        super()
        kind_ = keyword
        this.begin_ = kind_.pos
        this.end_ = Position(begin_.fileID, begin_.line, begin_.column + Int32(kind_.value.size))
    }
    public init() {
    }
    private var kind_: Token = Token()
    public mut prop keyword: Token {
        get() {
            kind_
        }
        set(v) {
            kind_ = v
        }
    }
    public func toTokens(): Tokens {
        return Tokens().append(kind_)
    }
    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        return
    }

    protected func dump(indent: UInt16): String {
        var ret: String = "Modifier {\n"
        ret += getTokenIndent("-keyword", kind_, indent + 1)
        ret += getIndent(indent) + "}\n"
        ret
    }
}

public class GenericParam <: Node {
    init(lAngle: Token, parameters: Tokens, rAngle: Token, commas: Tokens) {
        super()
        lAngle_ = lAngle
        parameters_ = parameters
        rAngle_ = rAngle
        commas_ = commas
    }
    public init(parameters: Tokens) {
        super()
        parameters_ = parameters
    }
    public init() {}
    private var lAngle_: Token = Token(TokenKind.LT)
    private var parameters_: Tokens = Tokens()
    private var rAngle_: Token = Token(TokenKind.GT)
    private var commas_: Tokens = Tokens()
    public mut prop lAngle: Token {
        get() {
            lAngle_
        }
        set(v) {
            checkTokenType(v, LT)
            lAngle_ = v
        }
    }
    public mut prop parameters: Tokens {
        get() {
            parameters_
        }
        set(v) {
            parameters_ = v
        }
    }
    public mut prop rAngle: Token {
        get() {
            rAngle_
        }
        set(v) {
            checkTokenType(v, GT)
            rAngle_ = v
        }
    }
    public func toTokens(): Tokens {
        var ret = Tokens()
        if (parameters_.size == 0) {
            return ret
        }
        ret.append(lAngle_)
        for (i in 0..parameters_.size - 1) {
            let comma = if (i >= commas_.size) {
                Token(TokenKind.COMMA)
            } else {
                commas_[i]
            }
            ret.append(parameters_[i]).append(comma)
        }
        return ret.append(parameters_[parameters_.size - 1]).append(rAngle_)
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        return
    }

    protected func dump(indent: UInt16): String {
        var ret: String = "GenericParam {\n"
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        for (i in 0..parameters_.size) {
            ret += getIndent(currentIndent) + "-parameters: ${i}, "
            ret += getTokenIndent(parameters_[i].value, parameters_[i], currentIndent, noNameIndent: true)
        }
        ret += getIndent(indent) + "}\n"
        ret
    }
}

/*
 * GenericConstraint is:
 *   where T <: A, K <: B
 */
public class GenericConstraint <: Node {
    init(keyword: Token, ty: TypeNode, upbound: Token, upperBounds: ArrayList<TypeNode>, bitAnds: Tokens) {
        super()
        keyword_ = keyword
        ty_ = ty
        upbound_ = upbound
        upperBounds_ = upperBounds
        bitAnds_ = bitAnds
    }

    public init() {}
    private var keyword_: Token = Token()
    private var ty_: TypeNode = RefType()
    private var upbound_: Token = Token(TokenKind.UPPERBOUND)
    private var upperBounds_: ArrayList<TypeNode> = ArrayList<TypeNode>()
    private var bitAnds_: Tokens = Tokens()
    public mut prop keyword: Token {
        get() {
            keyword_
        }
        set(v) {
            checkTokenType(v, WHERE)
            keyword_ = v
        }
    }
    public mut prop typeArgument: TypeNode {
        get() {
            ty_
        }
        set(v) {
            ty_ = v
        }
    }
    public mut prop upperBound: Token {
        get() {
            upbound_
        }
        set(v) {
            checkTokenType(v, UPPERBOUND)
            upbound_ = v
        }
    }
    public mut prop upperBounds: ArrayList<TypeNode> {
        get() {
            upperBounds_
        }
        set(v) {
            upperBounds_ = v
        }
    }
    public mut prop bitAnds: Tokens {
        get() {
            bitAnds_
        }
        set(v) {
            checkTokensType(v, BITAND)
            bitAnds_ = v
        }
    }
    public func toTokens(): Tokens {
        var ret = Tokens()
        if (isValidToken(keyword_)) {
            ret.append(keyword_)
        }
        ret.append(ty_.toTokens()).append(upbound_)
        for (i in 0..upperBounds_.size) {
            ret.append(upperBounds_[i].toTokens())
            if (i != upperBounds_.size - 1) {
                let bitAnd = bitAnds_.tryGet(i) ?? Token(TokenKind.BITAND)
                ret.append(bitAnd)
            }
        }
        ret
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        ty_.traverse(v)
        for (upb in upperBounds_) {
            upb.traverse(v)
        }
        return
    }

    protected func dump(indent: UInt16): String {
        var ret: String = "GenericConstraint {\n"
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        ret += getTokenIndent("-keyword", keyword_, currentIndent)
        ret += getIndent(currentIndent) + "-typeArgument: "
        ret += ty_.dump(currentIndent)
        ret += getTokenIndent("-upperBound", upbound_, currentIndent)
        for (i in 0..upperBounds_.size) {
            ret += getIndent(currentIndent) + "-upperBounds: ${i}, "
            ret += upperBounds_[i].dump(currentIndent)
        }
        ret += getIndent(indent) + "}\n"
        ret
    }
}

/*
 * Body is:
 *   {...}
 * For mutable types, like ClassDecl, StructDecl, InterfaceDecl
 */
public class Body <: Node {
    init(lBrace: Token, decls: ArrayList<Decl>, rBrace: Token) {
        super()
        lBrace_ = lBrace
        this.decls_ = decls
        rBrace_ = rBrace
    }
    public init(decls: ArrayList<Decl>) {
        super()
        this.decls_ = decls
    }
    public init() {
        super()
    }
    private var lBrace_: Token = Token(TokenKind.LCURL)
    private var decls_: ArrayList<Decl> = ArrayList<Decl>()
    private var rBrace_: Token = Token(TokenKind.RCURL)
    public mut prop lBrace: Token {
        get() {
            lBrace_
        }
        set(v) {
            checkTokenType(v, LCURL)
            lBrace_ = v
        }
    }
    public mut prop decls: ArrayList<Decl> {
        get() {
            decls_
        }
        set(v) {
            decls_ = v
        }
    }
    public mut prop rBrace: Token {
        get() {
            rBrace_
        }
        set(v) {
            checkTokenType(v, RCURL)
            rBrace_ = v
        }
    }
    public func toTokens(): Tokens {
        var ret: Tokens = Tokens()
        ret.append(lBrace_).append(Token(TokenKind.NL))
        for (decl in decls) {
            ret.append(decl.toTokens())
            if (ret[ret.size - 1].kind != NL) {
                ret.append(Token(NL).addPosition(decl.endPos))
            }
        }
        var rBracePos = rBrace_.pos
        // if last pos of ret equal to rbrace.pos, refresh rbrace.pos, for lexer will delete rbrace when see pos equal to node.endPos
        if (ret[ret.size - 1].pos == rBracePos) {
            rBracePos = Position(rBracePos.fileID, rBracePos.line, rBracePos.column + 1)
        }
        ret.append(((rBrace_)).addPosition(rBracePos))
        ret.append(Token(NL).addPosition(rBracePos.fileID, rBracePos.line, rBracePos.column + 1))
        return ret
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        for (decl in decls_) {
            decl.traverse(v)
        }
        return
    }

    protected func dump(indent: UInt16): String {
        var ret: String = "Body {\n"
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        for (i in 0..decls_.size) {
            ret += getIndent(currentIndent) + "-decls: ${i}, "
            ret += decls_[i].dump(currentIndent)
        }
        ret += getIndent(indent) + "}\n"
        ret
    }
}

/*
 * Argument is actual parameter Node:
 *   foo(arg1:value1)
 *   "arg1:value1" is an Argument type
 */
public class Argument <: Node {
    init(inout_: Token, identifier: Token, colon: Token, expr: Expr, comma: Token) {
        super()
        this.inout_ = inout_
        identifier_ = identifier
        colon_ = colon
        expr_ = expr
        comma_ = comma
    }
    public init() {}
    private var inout_: Token = Token()
    private var identifier_: Token = Token()
    private var colon_: Token = Token()
    private var expr_: Expr = Expr()
    private var comma_: Token = Token()
    public mut prop keyword: Token {
        get() {
            inout_
        }
        set(v) {
            inout_ = v
        }
    }
    public mut prop identifier: Token {
        get() {
            if (identifier_.kind != IDENTIFIER || identifier_.value.isEmpty()) {
                throw ASTException("Illegal Identifier")
            }
            identifier_
        }
        set(v) {
            if (v.kind != IDENTIFIER || v.value.isEmpty()) {
                throw ASTException("Illegal Identifier")
            }
            identifier_ = v
        }
    }
    public mut prop colon: Token {
        get() {
            colon_
        }
        set(v) {
            checkTokenType(v, COLON)
            colon_ = v
        }
    }
    public mut prop expr: Expr {
        get() {
            expr_
        }
        set(v) {
            expr_ = v
        }
    }

    public func toTokens(): Tokens {
        var ret = Tokens()
        if (isValidToken(inout_)) {
            ret.append(inout_)
        }
        if (identifier_.value.isEmpty()) {
            ret.append(expr_.toTokens())
        } else {
            ret.append(identifier)
            if (isValidToken(colon)) {
                ret.append(colon)
            } else {
                ret.append(Token(TokenKind.COLON))
            }
            ret.append(expr_.toTokens())
        }
        if (isValidToken(comma_)) {
            ret.append(comma_)
        }
        return ret
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        expr_.traverse(v)
        return
    }

    protected func dump(indent: UInt16): String {
        var ret: String = "Argument {\n"
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        ret += getTokenIndent("-keyword", inout_, currentIndent)
        ret += getTokenIndent("-identifier", identifier_, currentIndent)
        ret += getTokenIndent("-colon", colon_, currentIndent)
        ret += getIndent(currentIndent) + "-expr: "
        ret += expr_.dump(currentIndent)
        ret += getIndent(indent) + "}\n"
        ret
    }
}

/*
 * MatchCase is the children node of Match Expression:
 *   var scoreResult: String = match (score) {
 *       case 0 => "zero" // This is a MatchCase Node
 *       case 10 | 20 | 30 | 40 | 50 => "fail" // This is a MatchCase Node
 *       case _ => "not a valid score"
 *   }
 */
public class MatchCase <: Node {
    init(case_: Token, expr: Option<Expr>, arrow: Token, exprOrDecl: Block) {
        super()
        this.case_ = case_
        expr_ = expr
        arrow_ = arrow
        exprOrDecl_ = exprOrDecl
    }
    init(case_: Token, patterns: ArrayList<Pattern>, bitOrs: Tokens, where_: Token, patternGuard: Option<Expr>,
        arrow: Token, exprOrDecl: Block) {
        super()
        this.case_ = case_
        patterns_ = patterns
        bitOrs_ = bitOrs
        this.where_ = where_
        patternGuard_ = patternGuard
        arrow_ = arrow
        exprOrDecl_ = exprOrDecl
    }
    public init() {}
    private var case_: Token = Token(TokenKind.CASE)
    private var expr_: Option<Expr> = None<Expr>
    private var patterns_: ArrayList<Pattern> = ArrayList<Pattern>()
    private var bitOrs_: Tokens = Tokens()
    private var where_: Token = Token()
    private var patternGuard_: Option<Expr> = None<Expr>
    private var arrow_: Token = Token(TokenKind.DOUBLE_ARROW)
    private var exprOrDecl_: Block = Block()
    public mut prop keywordC: Token {
        get() {
            case_
        }
        set(v) {
            checkTokenType(v, CASE)
            case_ = v
        }
    }
    public mut prop expr: Expr {
        get() {
            match (expr_) {
                case Some(v) => v
                case None => throw ASTException("Cannot get expression from match case")
            }
        }
        set(v) {
            expr_ = v
        }
    }
    public mut prop patterns: ArrayList<Pattern> {
        get() {
            patterns_
        }
        set(v) {
            patterns_ = v
        }
    }
    public mut prop bitOrs: Tokens {
        get() {
            bitOrs_
        }
        set(v) {
            checkTokensType(v, BITOR)
            bitOrs_ = v
        }
    }
    public mut prop keywordW: Token {
        get() {
            where_
        }
        set(v) {
            checkTokenType(v, WHERE)
            where_ = v
        }
    }
    public mut prop patternGuard: Expr {
        get() {
            match (patternGuard_) {
                case Some(v) => v
                case None => throw ASTException("Cannot get patternGuard from match case")
            }
        }
        set(v) {
            patternGuard_ = Some(v)
        }
    }
    public mut prop arrow: Token {
        get() {
            arrow_
        }
        set(v) {
            checkTokenType(v, DOUBLE_ARROW)
            arrow_ = v
        }
    }
    public mut prop block: Block {
        get() {
            exprOrDecl_
        }
        set(v) {
            exprOrDecl_ = v
        }
    }
    public func toTokens(): Tokens {
        var ret = Tokens().append(case_)
        if (patterns_.isEmpty()) {
            match (expr_) {
                case Some(v) => ret.append(v).append(arrow_).append(exprOrDecl_)
                case None => ()
            }
        } else {
            for (i in 0..patterns_.size - 1) {
                let bitOr = if (i >= bitOrs.size) {
                    Token(TokenKind.BITOR)
                } else {
                    bitOrs[i]
                }
                ret.append(patterns_[i]).append(bitOr)
            }
            ret.append(patterns_[patterns_.size - 1])
            match (patternGuard_) {
                case Some(v) => ret.append(where_).append(v)
                case None => ()
            }
            ret.append(arrow_).append(exprOrDecl_)
        }
        ret
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        match (expr_) {
            case Some(node) => node.traverse(v)
            case None => ()
        }
        for (p in patterns_) {
            p.traverse(v)
        }
        match (patternGuard_) {
            case Some(node) => node.traverse(v)
            case None => ()
        }
        exprOrDecl_.traverse(v)
        return
    }

    protected func dump(indent: UInt16): String {
        var ret: String = "MatchCase {\n"
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        ret += getTokenIndent("-keywordC", case_, currentIndent)
        match (expr_) {
            case Some(v) =>
                ret += getIndent(currentIndent) + "-expr:"
                ret += v.dump(currentIndent)
            case None => ()
        }
        for (i in 0..patterns_.size) {
            ret += getIndent(currentIndent) + "-patterns: ${i}, "
            ret += patterns_[i].dump(currentIndent)
        }
        ret += getTokenIndent("-keywordW", where_, currentIndent)
        match (patternGuard_) {
            case Some(v) =>
                ret += getIndent(currentIndent) + "-patternGuard: "
                ret += v.dump(currentIndent)
            case None => ()
        }
        ret += getTokenIndent("-arrow", arrow_, currentIndent)
        ret += getIndent(currentIndent) + "-block: "
        ret += exprOrDecl_.dump(currentIndent)
        ret += getIndent(indent) + "}\n"
        ret
    }
}

/**
 * Program is the Cangjie source File node.
 */
public class Program <: Node {
    init(pkgHeader: PackageHeader, importLists: ArrayList<ImportList>,
        decls: ArrayList<Decl>, ftrDirective: Option<FeaturesDirective>) {
        super()
        pkgHeader_ = pkgHeader
        importLists_ = importLists
        decls_ = decls
        ftrDirective_ = ftrDirective
    }
    public init(inputs: Tokens) {
        try {
            var program = parseProgram(inputs)
            this.begin_ = program.beginPos
            this.end_ = program.endPos
            this.pkgHeader_ = program.pkgHeader_
            this.importLists_ = program.importLists_
            this.decls_ = program.decls_
            this.ftrDirective_ = program.ftrDirective_
        } catch (e: ParseASTException) {
            throw ASTException("Cannot construct the 'Program' node.")
        }
    }
    public init() {
    }

    private var pkgHeader_: PackageHeader = PackageHeader()
    private var importLists_: ArrayList<ImportList> = ArrayList<ImportList>()
    private var decls_: ArrayList<Decl> = ArrayList<Decl>()
    private var ftrDirective_: Option<FeaturesDirective> = None

    public mut prop featuresDirective: Option<FeaturesDirective> {
        get() {
            if (let Some(v) <- ftrDirective_) {
                return v
            }
            return None
        }
        set(v) {
            ftrDirective_ = v
        }
    }
    public mut prop packageHeader: PackageHeader {
        get() {
            pkgHeader_
        }
        set(v) {
            pkgHeader_ = v
        }
    }
    public mut prop importLists: ArrayList<ImportList> {
        get() {
            importLists_
        }
        set(v) {
            importLists_ = v
        }
    }
    public mut prop decls: ArrayList<Decl> {
        get() {
            decls_
        }
        set(v) {
            decls_ = v
        }
    }
    public func toTokens(): Tokens {
        var ret = Tokens()
        if (let Some(ftr) <- ftrDirective_) {
            ret.append(ftr.toTokens())
        }
        ret.append(pkgHeader_)
        if (!importLists_.isEmpty()) {
            ret.append(importLists_.toTokens())
        }
        for (decl in decls_) {
            ret.append(decl)
            if (ret[ret.size - 1].kind != NL) {
                ret.append(Token(NL).addPosition(decl.endPos))
            }
        }
        ret
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        if (let Some(ftr) <- ftrDirective_) {
            ftr.traverse(v)
        }
        pkgHeader_.traverse(v)
        for (im in importLists_) {
            im.traverse(v)
        }
        for (decl in decls_) {
            decl.traverse(v)
        }
        return
    }

    protected func dump(indent: UInt16): String {
        var ret: String = "Program {\n"
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        if (let Some(ftr) <- ftrDirective_) {
            ret += getIndent(currentIndent) + "-featuresDirective: "
            ret += ftr.dump(currentIndent + 1)
        }
        ret += getIndent(currentIndent) + "-packageHeader: "
        ret += pkgHeader_.dump(currentIndent + 1)
        for (i in 0..importLists_.size) {
            ret += getIndent(currentIndent) + "-importLists: ${i}, "
            ret += importLists_[i].dump(currentIndent)
        }
        for (i in 0..decls_.size) {
            ret += getIndent(currentIndent) + "-decls: ${i}, "
            ret += decls_[i].dump(currentIndent)
        }
        ret += getIndent(indent) + "}\n"
        ret
    }
}

public class FeatureId <: Node {
    init(identifiers: Tokens, dots: Tokens) {
        super()
        identifiers_ = identifiers
        dots_ = dots
    }

    public init() {}
    private var identifiers_: Tokens = Tokens()
    private var dots_: Tokens = Tokens()

    public mut prop identifiers: Tokens {
        get() {
            identifiers_
        }
        set(v) {
            checkTokensType(v, IDENTIFIER)
            identifiers_ = v
        }
    }
    public mut prop dots: Tokens {
        get() {
            dots_
        }
        set(v) {
            checkTokensType(v, DOT)
            dots_ = v
        }
    }

    public func toTokens(): Tokens {
        var ret = Tokens()
        var idx = 0
        for (ident in identifiers_) {
            ret.append(ident)
            if (idx < dots_.size) {
                ret.append(dots_[idx])
                idx++
            }
        }
        return ret
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        return
    }

    protected func dump(indent: UInt16): String {
        currentIndent = indent
        var ret: String = "FeatureId {\n"
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        var idx = 0
        for (tkn in identifiers_) {
            ret += getTokenIndent("-identifier", tkn, currentIndent)
            if (idx < dots_.size) {
                ret += getTokenIndent("-dot", dots_[idx], currentIndent)
                idx++
            }
        }
        ret += getIndent(indent) + "}\n"
        return ret
    }

}

public class FeaturesSet <: Node {
    init(lCurl: Token, content: ArrayList<FeatureId>, commas: Tokens, rCurl: Token) {
        super()
        this.lCurl_ = lCurl
        this.content_ = content
        this.commas_ = commas
        this.rCurl_ = rCurl
    }

    public init() {}
    private var lCurl_: Token = Token()
    private var content_: ArrayList<FeatureId> = ArrayList<FeatureId>()
    private var commas_: Tokens = Tokens()
    private var rCurl_: Token = Token()

    public mut prop lCurl: Token {
        get() {
            lCurl_
        }
        set(v) {
            checkTokenType(v, LCURL)
            lCurl_ = v
        }
    }
    public mut prop content: ArrayList<FeatureId> {
        get() {
            content_
        }
        set(v) {
            content_ = v
        }
    }
    public mut prop commas: Tokens {
        get() {
            commas_
        }
        set(v) {
            checkTokensType(v, COMMA)
            commas_ = v
        }
    }
    public mut prop rCurl: Token {
        get() {
            rCurl_
        }
        set(v) {
            checkTokenType(v, RCURL)
            rCurl_ = v
        }
    }

    public func toTokens(): Tokens {
        var ret = Tokens().append(lCurl_)
        var idx = 0
        for (item in content_) {
            ret.append(item.toTokens())
            if (idx < commas_.size) {
                ret.append(commas_[idx])
                idx++
            }
        }
        ret.append(rCurl_)
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        for (ftrId in content_) {
            ftrId.traverse(v)
        }
        return
    }

    protected func dump(indent: UInt16): String {
        currentIndent = indent
        var ret: String = "FeaturesSet {\n"
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        var idx = 0
        ret += getTokenIndent("-lCurl", lCurl_, currentIndent)
        for (item in content_) {
            ret += getIndent(currentIndent) + "-FeatureId: "
            ret += item.dump(currentIndent)
            if (idx < commas_.size) {
                ret += getTokenIndent("-comma: ", commas_[idx], currentIndent)
                idx++
            }
        }
        ret += getTokenIndent("-rCurl", rCurl_, currentIndent)
        ret += getIndent(indent) + "}\n"
        return ret
    }
}

public class FeaturesDirective <: Node {
    init(annotations: ArrayList<Annotation>, keyword: Token, featuresSet: FeaturesSet) {
        super()
        this.annotations_ = annotations
        this.keyword_ = keyword
        this.featuresSet_ = featuresSet
    }

    public init(inputs: Tokens) {
        try {
            let features = parseProgram(inputs).featuresDirective
            let ftr = features ?? throw ParseASTException()
            this.begin_ = ftr.beginPos
            this.end_ = ftr.endPos
            this.annotations_ = ftr.annotations
            this.keyword_ = ftr.keyword
            this.featuresSet_= ftr.featuresSet
        } catch (e: ParseASTException) {
            throw ASTException("Cannot construct the 'FeaturesDirective' node.")
        }
    }

    public init() {}
    private var annotations_: ArrayList<Annotation> = ArrayList<Annotation>()
    private var keyword_: Token = Token()
    private var featuresSet_: FeaturesSet = FeaturesSet()

    public mut prop annotations: ArrayList<Annotation> {
        get() {
            annotations_
        }
        set(v) {
            annotations_ = v
        }
    }
    public mut prop keyword: Token {
        get() {
            keyword_
        }
        set(v) {
            checkTokenType(v, FEATURES)
            keyword_ = v
        }
    }
    public mut prop featuresSet: FeaturesSet {
        get() {
            featuresSet_
        }
        set(v) {
            featuresSet_ = v
        }
    }

    public func toTokens(): Tokens {
        var ret = Tokens()
        for (anno in annotations_) {
            ret.append(anno.toTokens())
        }
        ret.append(keyword_)
        ret.append(featuresSet_.toTokens())
        let endTok = ret[ret.size - 1]
        let endPos = endTok.pos
        ret.append(Token(TokenKind.NL).addPosition(endPos.fileID, endPos.line, endPos.column + Int32(endTok.value.size)))
        return ret
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        featuresSet_.traverse(v)
        return
    }

    protected func dump(indent: UInt16): String {
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        var ret: String = "FeaturesDirective {\n"
        for (i in 0..annotations_.size) {
            ret += getIndent(currentIndent) + "-annotation: ${i}, "
            ret += annotations_[i].dump(currentIndent)
        }
        ret += getTokenIndent("-keyword", keyword_, currentIndent)
        ret += getIndent(currentIndent) + "-featuresSet: "
        ret += featuresSet_.dump(indent + 1)
        ret += getIndent(indent) + "}\n"
        return ret
    }
}

/**
 * PackageHeader node represents the package of the current file.
 * The PackageHeader starts with the keyword package, followed by the package name.
 */
public class PackageHeader <: Node {
    init(accessibleToken: Token, macroToken: Token, keyword: Token, orgName: Token, orgToken: Token, prefixPaths: Tokens, prefixDots: Tokens,
        pkgIdentifier: Token) {
        super()
        accessibleToken_ = accessibleToken
        macroToken_ = macroToken
        keyword_ = keyword
        orgName_ = orgName
        orgToken_ = orgToken
        prefixPaths_ = prefixPaths
        prefixDots_ = prefixDots
        pkgIdentifier_ = pkgIdentifier
    }
    public init(inputs: Tokens) {
        try {
            var pkg = parseProgram(inputs).packageHeader
            this.begin_ = pkg.beginPos
            this.end_ = pkg.endPos
            this.accessibleToken_ = pkg.accessibleToken_
            this.macroToken_ = checkValid(pkg.macroToken_)
            this.keyword_ = pkg.keyword_
            this.orgName_ = checkValid(pkg.orgName_)
            this.orgToken_ = checkValid(pkg.orgToken_)
            this.prefixPaths_ = pkg.prefixPaths_
            this.prefixDots_ = pkg.prefixDots_
            this.pkgIdentifier_ = pkg.pkgIdentifier_
        } catch (e: ParseASTException) {
            throw ASTException("Cannot construct the 'PackageHeader' node.")
        }
    }
    public init() {
        pkgIdentifier_ = Token(TokenKind.IDENTIFIER, "default")
    }
    init(isFromDerivation: Bool) {
        pkgIdentifier_ = Token(TokenKind.IDENTIFIER, "default")
        isFromDerivation_ = isFromDerivation
    }
    private var accessibleToken_: Token = Token(TokenKind.PUBLIC)
    private var macroToken_: Token = Token()
    private var keyword_: Token = Token(TokenKind.PACKAGE)
    private var orgName_: Token = Token()
    private var orgToken_: Token = Token()
    private var prefixPaths_: Tokens = Tokens()
    private var prefixDots_: Tokens = Tokens()
    private var pkgIdentifier_: Token = Token(TokenKind.IDENTIFIER, "")
    private var isFromDerivation_ = false
    public mut prop accessible: Token {
        get() {
            accessibleToken_
        }
        set(v) {
            accessibleToken_ = v
        }
    }
    public mut prop keywordM: Token {
        get() {
            macroToken_
        }
        set(v) {
            checkTokenType(v, MACRO)
            macroToken_ = v
        }
    }
    public mut prop keywordP: Token {
        get() {
            keyword_
        }
        set(v) {
            checkTokenType(v, PACKAGE)
            keyword_ = v
        }
    }

    /**
     * @brief Organization name of the package.
     * @throws ASTException if the input token 'v' has empty value.
     */
    public mut prop orgName: Token {
        get() {
            orgName_
        }
        set(v) {
            checkTokenType(v, IDENTIFIER)
            if (v.value == "") {
                throw ASTException("orgName should not be empty")
            }
            if (!isValidPosition(orgToken_)) {
                orgToken_ = Token(TokenKind.DOUBLE_COLON)
            }
            orgName_ = v
        }
    }

    /**
     * @brief The DOUBLE_COLON token separating org name and package path.
     * @throws ASTException if the input token 'v' has empty value or 'orgName_' is not valid.
     */
    public mut prop orgSeparator: Token {
        get() {
            orgToken_
        }
        set(v) {
            checkTokenType(v, DOUBLE_COLON)
            if (orgName_.value == "" || !isValidPosition(orgName_)) {
                throw ASTException("orgName should not be empty")
            }
            orgToken_ = v
        }
    }
    public mut prop prefixPaths: Tokens {
        get() {
            prefixPaths_
        }
        set(v) {
            prefixPaths_ = v
        }
    }
    public mut prop prefixDots: Tokens {
        get() {
            prefixDots_
        }
        set(v) {
            checkTokensType(v, DOT)
            prefixDots_ = v
        }
    }
    public mut prop packageIdentifier: Token {
        get() {
            pkgIdentifier_
        }
        set(v) {
            pkgIdentifier_ = v
        }
    }
    public func toTokens(): Tokens {
        var ret = Tokens()
        if (!isFromDerivation_) {
            if (isValidPosition(macroToken_)) {
                ret.append(macroToken_)
            }
            ret.append(keyword_)
            if (isValidPosition(orgToken_)) {
                ret.append(orgName_)
                ret.append(orgToken_)
            }
            ret = addPackageTokens(ret)
            let identPos = pkgIdentifier_.pos
            let identColumn = identPos.column + Int32(pkgIdentifier_.value.size)
            ret.append(Token(TokenKind.NL).addPosition(identPos.fileID, identPos.line, identColumn))
        }
        ret
    }
    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        return
    }

    protected func dump(indent: UInt16): String {
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        var ret: String = "PackageHeader {\n"
        if (isValidToken(accessibleToken_)) {
            ret += getTokenIndent("-modifier", accessibleToken_, currentIndent)
        }
        if (isValidPosition(macroToken_)) {
            ret += getTokenIndent("-keywordM", macroToken_, currentIndent)
        }
        ret += getTokenIndent("-keywordP", keyword_, currentIndent)
        if (isValidPosition(orgToken_)) {
            ret += getTokenIndent("-orgName", orgName_, currentIndent)
            ret += getTokenIndent("-orgSepartaor", orgToken_, currentIndent)
        }
        for (i in 0..prefixPaths_.size) {
            ret += getTokenIndent("prefixPath: ${i}", prefixPaths_[i], currentIndent)
            if (i < prefixDots_.size) {
                ret += getTokenIndent("dot: ${i}", prefixDots_[i], currentIndent)
            } else {
                ret += getTokenIndent("dot: ${i}", Token(TokenKind.DOT), currentIndent)
            }
        }
        ret += getTokenIndent("-packageIdentifier", pkgIdentifier_, currentIndent)
        ret += getIndent(indent) + "}\n"
        ret
    }

    func addPackageTokens(input: Tokens): Tokens {
        for (i in 0..prefixPaths_.size) {
            input.append(prefixPaths_[i])
            if (i < prefixDots_.size) {
                input.append(prefixDots_[i])
            } else {
                input.append(Token(TokenKind.DOT))
            }
        }
        input.append(pkgIdentifier_)
    }
}

public enum ImportKind <: ToString {
    | Single 
    | Alias
    | All
    | Multi

    public func toString(): String {
        return match (this) {
            case Single => "Single"
            case Alias => "Alias"
            case All => "All"
            case Multi => "Multi"
        }
    }
}

public class ImportContent <: Node {
    init(importKind: ImportKind, orgName: Token, orgToken: Token, prefixPaths: Tokens, prefixDots: Tokens, identifier: Token, importAlias: Tokens,
        leftCurl: Token, items: ArrayList<ImportContent>, commas: Tokens, rightCurl: Token) {
        super()
        importKind_ = importKind
        orgName_ = orgName
        orgToken_ = orgToken
        prefixPaths_ = prefixPaths
        prefixDots_ = prefixDots
        identifier_ = identifier
        importAlias_ = importAlias
        leftCurl_ = leftCurl
        items_ = items
        commas_ = commas
        rightCurl_ = rightCurl
    }
    public init() {}
    private var importKind_: ImportKind = ImportKind.Single
    private var orgName_: Token = Token()
    private var orgToken_: Token = Token()
    private var prefixPaths_: Tokens = Tokens()
    private var prefixDots_: Tokens = Tokens()
    private var identifier_: Token = Token()
    private var importAlias_: Tokens = Tokens()
    private var leftCurl_: Token = Token()
    private var items_: ArrayList<ImportContent> = ArrayList<ImportContent>()
    private var commas_: Tokens = Tokens()
    private var rightCurl_: Token = Token()
    private let innerToken_: Tokens = Token(TokenKind.NL) + quote(main() {})

    public mut prop importKind: ImportKind {
        get() {
            importKind_
        }
        set(v) {
            importKind_ = v
        }
    }

    /**
     * @brief Organization name of the package.
     * @throws ASTException if the input token 'v' has empty value.
     */
    public mut prop orgName: Token {
        get() {
            orgName_
        }
        set(v) {
            checkTokenType(v, IDENTIFIER)
            if (v.value == "") {
                throw ASTException("orgName should not be empty")
            }
            if (!isValidPosition(orgToken_)) {
                orgToken_ = Token(TokenKind.DOUBLE_COLON)
            }
            orgName_ = v
        }
    }

    /**
     * @brief The DOUBLE_COLON token separating org name and package path.
     * @throws ASTException if the input token 'v' has empty value or 'orgName_' is not valid.
     */
    public mut prop orgSeparator: Token {
        get() {
            orgToken_
        }
        set(v) {
            checkTokenType(v, DOUBLE_COLON)
            if (orgName_.value == "" || !isValidPosition(orgName_)) {
                throw ASTException("orgName should not be empty")
            }
            orgToken_ = v
        }
    }
    public mut prop prefixPaths: Tokens {
        get() {
            prefixPaths_
        }
        set(v) {
            prefixPaths_ = v
        }
    }
    public mut prop prefixDots: Tokens {
        get() {
            prefixDots_
        }
        set(v) {
            checkTokensType(v, DOT)
            prefixDots_ = v
        }
    }
    public mut prop identifier: Token {
        get() {
            identifier_
        }
        set(v) {
            identifier_ = v
        }
    }
    public mut prop importAlias: Tokens {
        get() {
            importAlias_
        }
        set(v) {
            importAlias_ = v
        }
    }
    public mut prop lBrace: Token {
        get() {
            leftCurl_
        }
        set(v) {
            checkTokenType(v, LCURL)
            leftCurl_ = v
        }
    }
    public mut prop items: ArrayList<ImportContent> {
        get() {
            items_
        }
        set(v) {
            items_ = v
        }
    }
    public mut prop commas: Tokens {
        get() {
            commas_
        }
        set(v) {
            checkTokensType(v, COMMA)
            commas_ = v
        }
    }
    public mut prop rBrace: Token {
        get() {
            rightCurl_
        }
        set(v) {
            checkTokenType(v, RCURL)
            rightCurl_ = v
        }
    }
    public func isImportAlias(): Bool {
        return if (let ImportKind.Alias <- importKind_) {
            true
        } else {
            false
        }
    }
    public func isImportAll(): Bool {
        return if (let ImportKind.All <- importKind_) {
            true
        } else {
            false
        }
    }
    public func isImportMulti(): Bool {
        return if (let ImportKind.Multi <- importKind_) {
            true
        } else {
            false
        }
    }
    public func isImportSingle(): Bool {
        return if (let ImportKind.Single <- importKind_) {
            true
        } else {
            false
        }
    }
    public func toTokens(): Tokens {
        var ret = Tokens()
        if (isValidPosition(orgToken_)) {
            ret.append(orgName_)
            ret.append(orgToken_)
        }
        for (i in 0..prefixPaths_.size) {
            let dot = if (i >= prefixDots_.size) {
                Token(TokenKind.DOT)
            } else {
                prefixDots_[i]
            }
            ret.append(prefixPaths_[i]).append(dot)
        }
        if (!isImportMulti()) {
            ret.append(identifier)
        }
        if (isImportAll() || isImportSingle()) {
            return ret
        }
        if (isImportAlias()) {
            ret.append(importAlias_)
            return ret
        }
        ret.append(leftCurl_)
        for (i in 0..items_.size) {
            ret.append(items_[i].toTokens())
            if (i < commas_.size && isValidToken(commas_[i])) {
                ret.append(commas_[i])
            }
        }
        ret.append(rightCurl_)
        ret
    }
    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        for (item in items_) {
            item.traverse(v)
        }
        return
    }

    protected func dump(indent: UInt16): String {
        currentIndent = indent
        var ret: String = ""
        ret += getIndent(currentIndent) + "-importKind: ${importKind_}\n"
        if (isValidPosition(orgToken_)) {
            ret += getTokenIndent("-orgName", orgName_, currentIndent)
            ret += getTokenIndent("-orgSepartaor", orgToken_, currentIndent)
        }
        for (i in 0..prefixPaths_.size) {
            ret += getTokenIndent("prefixPath: ${i}", prefixPaths_[i], currentIndent)
        }
        ret += getTokenIndent("-identifier", identifier_, currentIndent)
        if (isImportAll() || isImportSingle()) {
            return ret
        }
        if (isImportAlias()) {
            for (i in 0..importAlias_.size) {
                ret += getTokenIndent("-importAlias ${i}", importAlias_[i], currentIndent)
            }
            return ret
        }
        ret += getTokenIndent("-leftCurl", leftCurl_, currentIndent)
        for (i in 0..items_.size) {
            ret += items_[i].dump(currentIndent + 1)
            if (i < commas_.size && isValidToken(commas_[i])) {
                ret += getTokenIndent("-comma", commas_[i], currentIndent)
            }
        }
        ret += getTokenIndent("-rightCurl", rightCurl_, currentIndent)
        ret
    }
}

public class ImportList <: Node {
    init(reExportToken: Token, imp: Token, content: ImportContent) {
        super()
        reExportToken_ = reExportToken
        import_ = imp
        content_ = content
    }
    public init(inputs: Tokens) {
        try {
            let importLists = parseProgram(inputs.append(innerToken_)).importLists
            if (importLists.size == 0) {
                throw ASTException("Cannot construct the 'ImportList' node.")
            }
            let importList = importLists[0]
            this.begin_ = importList.beginPos
            this.end_ = importList.endPos
            this.reExportToken_ = importList.reExportToken_
            this.import_ = importList.import_
            this.content_ = importList.content_
        } catch (e: ParseASTException) {
            throw ASTException("Cannot construct the 'ImportList' node.")
        }
    }
    public init() {}
    private var reExportToken_: Token = Token()
    private var import_: Token = Token(TokenKind.IMPORT)
    private var content_: ImportContent = ImportContent()
    private let innerToken_: Tokens = Token(TokenKind.NL) + quote(main() {})

    public mut prop modifier: Token {
        get() {
            reExportToken_
        }
        set(v) {
            reExportToken_ = v
        }
    }
    public mut prop keywordI: Token {
        get() {
            import_
        }
        set(v) {
            import_ = v
        }
    }
    public mut prop content: ImportContent {
        get() {
            content_
        }
        set(v) {
            content_ = v
        }
    }
    public func isImportMulti(): Bool {
        return content_.isImportMulti()
    }
    public func toTokens(): Tokens {
        var ret = Tokens()
        if (import_.pos != beginPos && isValidToken(reExportToken_)) {
            ret.append(reExportToken_)
        }
        ret.append(import_)
        ret.append(content_.toTokens())
        let endTok = ret[ret.size - 1]
        let endPos = endTok.pos
        ret.append(Token(TokenKind.NL).addPosition(endPos.fileID, endPos.line, endPos.column + Int32(endTok.value.size)))
        ret
    }
    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        content_.traverse(v)
        return
    }

    protected func dump(indent: UInt16): String {
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        var ret: String = "ImportList {\n"
        if (isValidToken(reExportToken_)) {
            ret += getTokenIndent("-modifier", reExportToken_, currentIndent)
        }
        ret += getTokenIndent("-keywordI", import_, currentIndent)
        ret += content_.dump(currentIndent)
        ret += getIndent(indent) + "}\n"
        ret
    }
}

/**
 * Constructor node represents the values related to enum.
 * enum TimeUnit1 {
 *   Year | Month | Day | Hour(Float32) // Constructor node
 * }
 */
public class Constructor <: Node {
    public init() {
    }
    init(annotations: ArrayList<Annotation>, identifier: Token) {
        super()
        annotations_ = annotations
        identifier_ = identifier
    }
    init(annotations: ArrayList<Annotation>, identifier: Token, lParen: Token, params: ArrayList<TypeNode>,
        commas: Tokens, rParen: Token) {
        super()
        annotations_ = annotations
        identifier_ = identifier
        lParen_ = lParen
        params_ = params
        commas_ = commas
        rParen_ = rParen
    }
    private var identifier_: Token = Token(TokenKind.IDENTIFIER, "")
    private var lParen_: Token = Token()
    private var params_: ArrayList<TypeNode> = ArrayList<TypeNode>()
    private var commas_: Tokens = Tokens()
    private var rParen_: Token = Token()
    private var annotations_: ArrayList<Annotation> = ArrayList<Annotation>()
    public mut prop annotations: ArrayList<Annotation> {
        get() {
            annotations_
        }
        set(v) {
            annotations_ = v
        }
    }
    public mut prop identifier: Token {
        get() {
            identifier_
        }
        set(v) {
            identifier_ = v
        }
    }
    public mut prop lParen: Token {
        get() {
            lParen_
        }
        set(v) {
            checkTokenType(v, LPAREN)
            lParen_ = v
        }
    }
    public mut prop typeArguments: ArrayList<TypeNode> {
        get() {
            params_
        }
        set(v) {
            params_ = v
        }
    }
    public mut prop rParen: Token {
        get() {
            rParen_
        }
        set(v) {
            checkTokenType(v, RPAREN)
            rParen_ = v
        }
    }
    public func toTokens(): Tokens {
        var ret = Tokens()
        for (i in 0..annotations_.size) {
            ret.append(annotations_[i].toTokens())
        }
        ret.append(identifier_)
        if (!params_.isEmpty()) {
            ret.append(lParen_)
            if (commas_.size == 0) {
                commas_ = Tokens(Array(params_.size - 1) {
                    i => Token(TokenKind.COMMA).addPosition(
                        params_[i].endPos.fileID,
                        params_[i].endPos.line,
                        params_[i].endPos.column + 1
                    )
                })
            }

            let lastParamIdx = params_.size - 1
            for (i in 0..lastParamIdx) {
                ret.append(params_[i]).append(commas_[i])
            }
            ret.append(params_[lastParamIdx])
            ret.append(rParen_)
        }
        ret
    }

    public func traverse(v: Visitor): Unit {
        v.visit(this)
        super.traverse(v)
        if (v.needBreakTraverse()) {
            return
        }
        for (anno in annotations_) {
            anno.traverse(v)
        }
        for (param in params_) {
            param.traverse(v)
        }
        return
    }

    protected func dump(indent: UInt16): String {
        var ret: String = "Constructor {\n"
        currentIndent = indent
        if (indent != 0 || beginNode) {
            currentIndent++
        }
        for (i in 0..annotations_.size) {
            ret += getIndent(currentIndent) + "-annotations: ${i}, "
            ret += annotations_[i].dump(currentIndent)
        }
        ret += getTokenIndent("-identifier", identifier_, currentIndent)
        for (i in 0..params_.size) {
            ret += getIndent(currentIndent) + "-typeArguments: ${i}, "
            ret += params_[i].dump(currentIndent)
        }
        ret += getIndent(indent) + "}\n"
        ret
    }
}

extend<T> ArrayList<T> <: ToTokens {
    public func toTokens(): Tokens {
        var ret: Tokens = Tokens()
        match (this) {
            case decls: ArrayList<Decl> =>
                for (i in 0..decls.size) {
                    ret.append(decls[i].toTokens()).append(Token(TokenKind.NL))
                }
                ret
            case nodes: ArrayList<Node> =>
                for (i in 0..nodes.size) {
                    ret.append(nodes[i].toTokens())
                    if (i != nodes.size - 1) {
                        ret.append(Token(TokenKind.NL))
                    }
                }
                ret
            case constructors: ArrayList<Constructor> =>
                for (i in 0..constructors.size) {
                    ret.append(constructors[i].toTokens())
                    if (i != constructors.size - 1) {
                        ret.append(Token(TokenKind.BITOR))
                    }
                }
                ret
            case arguments: ArrayList<Argument> =>
                for (i in 0..arguments.size) {
                    ret.append(arguments[i].toTokens())
                    if (ret.size != 0 && ret[ret.size - 1].kind != COMMA && i != arguments.size - 1) {
                        ret.append(Token(COMMA))
                    }
                }
                if (ret.size != 0 && ret[ret.size - 1].kind == COMMA) {
                    ret.remove(ret.size - 1)
                }
                ret
            case funcParams: ArrayList<FuncParam> =>
                for (i in 0..funcParams.size) {
                    ret.append(funcParams[i].toTokens())
                    if (ret.size != 0 && ret[ret.size - 1].kind != COMMA && i != funcParams.size - 1) {
                        ret.append(Token(COMMA))
                    }
                }
                if (ret.size != 0 && ret[ret.size - 1].kind == COMMA) {
                    ret.remove(ret.size - 1)
                }
                ret
            case matchCases: ArrayList<MatchCase> =>
                for (m in matchCases) {
                    ret.append(m)
                }
                ret
            case modifiers: ArrayList<Modifier> =>
                for (i in 0..modifiers.size) {
                    ret.append(modifiers[i].toTokens())
                }
                ret
            case annotations: ArrayList<Annotation> =>
                for (i in 0..annotations.size) {
                    ret.append(annotations[i])
                }
                ret
            case importLists: ArrayList<ImportList> => getImportSpecTokens(importLists)
            case patterns: ArrayList<Pattern> =>
                for (i in 0..patterns.size) {
                    ret.append(patterns[i].toTokens())
                    if (i != patterns.size - 1) {
                        ret.append(Token(TokenKind.BITOR))
                    }
                }
                ret
            case tys: ArrayList<TypeNode> =>
                for (i in 0..tys.size) {
                    ret.append(tys[i].toTokens())
                    if (i != tys.size - 1) {
                        ret.append(Token(TokenKind.BITAND))
                    }
                }
                ret
            case _ => throw IllegalArgumentException("Unsupported ArrayList<T> in function toTokens().\n")
        }
    }
}

extend<T> Array<T> <: ToTokens {
    public func toTokens(): Tokens {
        match (this) {
            case elems: Array<Int8> =>
                var rInt8: Tokens = Tokens()
                rInt8.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rInt8.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rInt8.append(Token(TokenKind.COMMA))
                    }
                }
                rInt8.append(Token(TokenKind.RSQUARE))
            case elems: Array<Int16> =>
                var rInt16: Tokens = Tokens()
                rInt16.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rInt16.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rInt16.append(Token(TokenKind.COMMA))
                    }
                }
                rInt16.append(Token(TokenKind.RSQUARE))
            case elems: Array<Int32> =>
                var rInt32: Tokens = Tokens()
                rInt32.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rInt32.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rInt32.append(Token(TokenKind.COMMA))
                    }
                }
                rInt32.append(Token(TokenKind.RSQUARE))
            case elems: Array<Int64> =>
                var rInt64: Tokens = Tokens()
                rInt64.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rInt64.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rInt64.append(Token(TokenKind.COMMA))
                    }
                }
                rInt64.append(Token(TokenKind.RSQUARE))
            case elems: Array<UInt8> =>
                var rUInt8: Tokens = Tokens()
                rUInt8.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rUInt8.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rUInt8.append(Token(TokenKind.COMMA))
                    }
                }
                rUInt8.append(Token(TokenKind.RSQUARE))
                rUInt8
            case elems: Array<UInt16> =>
                var rUInt16: Tokens = Tokens()
                rUInt16.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rUInt16.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rUInt16.append(Token(TokenKind.COMMA))
                    }
                }
                rUInt16.append(Token(TokenKind.RSQUARE))
            case elems: Array<UInt32> =>
                var rUInt32: Tokens = Tokens()
                rUInt32.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rUInt32.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rUInt32.append(Token(TokenKind.COMMA))
                    }
                }
                rUInt32.append(Token(TokenKind.RSQUARE))
            case elems: Array<UInt64> =>
                var rUInt64: Tokens = Tokens()
                rUInt64.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rUInt64.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rUInt64.append(Token(TokenKind.COMMA))
                    }
                }
                rUInt64.append(Token(TokenKind.RSQUARE))
            case elems: Array<Rune> =>
                var rChar: Tokens = Tokens()
                rChar.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rChar.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rChar.append(Token(TokenKind.COMMA))
                    }
                }
                rChar.append(Token(TokenKind.RSQUARE))
            case elems: Array<Bool> =>
                var rBool: Tokens = Tokens()
                rBool.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rBool.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rBool.append(Token(TokenKind.COMMA))
                    }
                }
                rBool.append(Token(TokenKind.RSQUARE))
            case elems: Array<String> =>
                var rString: Tokens = Tokens()
                rString.append(Token(TokenKind.LSQUARE))
                for (i in 0..elems.size) {
                    rString.append(elems[i].toTokens())
                    if (i != elems.size - 1) {
                        rString.append(Token(TokenKind.COMMA))
                    }
                }
                rString.append(Token(TokenKind.RSQUARE))
            case _ => throw IllegalArgumentException("Unsupported Array<T> input in function toTokens().\n")
        }
    }
}