/*
 * 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, collectArray, map}
import std.convert

@When[!debug]
private func fail(info: String) {
}

@When[debug]
private func fail(info: String) {
    throw Exception("ParseException: " + info)
}

class SyntaxNodeImplTranslator {
    private init() {
    }

    static func translate(root: SyntaxNodeImpl, startPos: CodePosition, parent: ?SyntaxTreeNode): ?SyntaxTreeNode {
        match (root.kind) {
            case SyntaxNodeKind.File => SyntaxNodeImplTranslator().transFileNode(root, startPos.filePath, parent: parent)
            case _ => SyntaxNodeImplTranslator().transNode(root, startPos, parent)
        }
    }

    static func translateList<T>(root: SyntaxNodeImpl, startPos: CodePosition, parent: ?SyntaxTreeNode): Array<T> where T <: SyntaxTreeNode {
        SyntaxNodeImplTranslator().transList<T>(root, startPos, parent)
    }

    static func translateFile(root: SyntaxNodeImpl, filePath: String, parent!: ?SyntaxTreeNode = None): ?SyntaxTreeNode {
        SyntaxNodeImplTranslator().transFileNode(root, filePath, parent: parent)
    }

    private func addList<T>(nodes: ArrayList<T>, node: ?SyntaxTreeNode) where T <: SyntaxTreeNode {
        if (let Some(value) <- node) {
            if (let Some(target) <- (value as T)) {
                nodes.add(target)
            } else {
                fail("type cast fail")
            }
        } else {
            fail("the node is None!")
        }
    }

    // General conversion function between the two types
    private func cast<T, S>(node: ?S): ?T {
        if (let Some(value) <- node) {
            value as T
        } else {
            None
        }
    }

    private func isForeignModifier(node: SyntaxNodeImpl): Bool {
        let modi = (node as NonTerminal).getOrThrow()
        for (child in modi.children) {
            if (child.kind == SyntaxNodeKind.ForeignToken) {
                return true
            }
        }
        false
    }

    private func transList<T>(node: SyntaxNodeImpl, startPos: CodePosition, parent: ?SyntaxTreeNode): Array<T> where T <: SyntaxTreeNode {
        var result = ArrayList<T>()
        match (node) {
            case nt: NonTerminal =>
                var beginPos = startPos
                for (child in nt.children) {
                    if (child.kind == SyntaxNodeKind.Modifier && isForeignModifier(child)) {
                        continue
                    }
                    match (child.kind) {
                        case SyntaxNodeKind.NewlineToken | SyntaxNodeKind.SpaceToken => beginPos += child.offset
                        case _ =>
                            addList<T>(result, transNode(child, beginPos, parent))
                            beginPos += child.offset
                    }
                }
            case _ => fail("not support terminal for transList")
        }
        result.toArray()
    }

    private func transFileNode(node: SyntaxNodeImpl, filePath: String, parent!: ?SyntaxTreeNode = None): ?SyntaxTreeNode {
        let ret: ?SyntaxTreeNode = match (node) {
            case nt: NonTerminal => transFile(nt, filePath, parent: parent)
            case _ => None
        }
        ret
    }

    private func getBeginPos(node: SyntaxNodeImpl, startPos: CodePosition): CodePosition {
        let lp = LocalParser(node.preWhiteSpace)
        lp.consumeWhitespace()
        return startPos + lp.offset
    }

    // Recursive translation
    private func transNode(node: SyntaxNodeImpl, startPos: CodePosition, parent: ?SyntaxTreeNode): ?SyntaxTreeNode {
        let beginPos = getBeginPos(node, startPos)
        let ret: ?SyntaxTreeNode = match (node) {
            case nt: NonTerminal => transNonTerminal(nt, beginPos, parent)
            case _ =>
                fail("terminal node kind - ${node.kind.toString()} - ${node.toString()} need to be translated");
                None
        }
        ret
    }

    func transNonTerminal(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ?SyntaxTreeNode {
        match (node.kind) {
            case SyntaxNodeKind.GenericParam => transGenericParam(node, startPos, parent)
            case SyntaxNodeKind.PrimitiveType => transAtomicType(node, startPos, parent)
            case SyntaxNodeKind.ValuedLiteral => transValuedLitConstExpr(node, startPos, parent)
            case SyntaxNodeKind.Modifier => transModifier(node, startPos, parent)
            case SyntaxNodeKind.Comment => transComment(node, startPos, parent)
            case SyntaxNodeKind.CommentGroup => transCommentGroup(node, startPos, parent)
            case SyntaxNodeKind.Argument => transArgument(node, startPos, parent)
            case SyntaxNodeKind.Annotation => transAnnotation(node, startPos, parent)
            case SyntaxNodeKind.CatchPattern => transCatchPattern(node, startPos, parent)
            case SyntaxNodeKind.PackageSpec => transPackageHeader(node, startPos, parent)
            case SyntaxNodeKind.GenericConstraints => transGenericConstraints(node, startPos, parent)
            case SyntaxNodeKind.GenericConstraint => transGenericConstraint(node, startPos, parent)
            case SyntaxNodeKind.DisjunctionCondition => transDisjunctionCondition(node, startPos, parent)
            case SyntaxNodeKind.ConjunctionCondition => transConjunctionCondition(node, startPos, parent)
            case SyntaxNodeKind.ParenCondition => transParenCondition(node, startPos, parent)
            case SyntaxNodeKind.LetPattern => transLetPattern(node, startPos, parent)
            case SyntaxNodeKind.FuncDecl => transFuncDecl(node, startPos, parent)
            case SyntaxNodeKind.VarDecl => transVarDecl(node, startPos, parent)
            case SyntaxNodeKind.MainDecl => transMainDecl(node, startPos, parent)
            case SyntaxNodeKind.TypeAlias => transTypeAlias(node, startPos, parent)
            case SyntaxNodeKind.Body => transBody(node, startPos, parent)
            case SyntaxNodeKind.ClassDecl => transClassDecl(node, startPos, parent)
            case SyntaxNodeKind.PropDecl => transPropDecl(node, startPos, parent)
            case SyntaxNodeKind.PropGetterOrSetter => transPropGetterOrSetter(node, startPos, parent)
            case SyntaxNodeKind.StaticInit => transStaticInit(node, startPos, parent)
            case SyntaxNodeKind.StructDecl => transStructDecl(node, startPos, parent)
            case SyntaxNodeKind.InterfaceDecl => transInterfaceDecl(node, startPos, parent)
            case SyntaxNodeKind.ExtendDecl => transExtendDecl(node, startPos, parent)
            case SyntaxNodeKind.EnumDecl => transEnumDecl(node, startPos, parent)
            case SyntaxNodeKind.EnumConstructor => transEnumConstructor(node, startPos, parent)
            case SyntaxNodeKind.MacroDecl => transMacroDecl(node, startPos, parent)
            case SyntaxNodeKind.MacroExpandDecl => transMacroExpandDecl(node, startPos, parent)
            case SyntaxNodeKind.MacroExpandParam => transMacroExpandParam(node, startPos, parent)
            case SyntaxNodeKind.MacroExpandExpr => transMacroExpandExpr(node, startPos, parent)
            case SyntaxNodeKind.Block => transBlock(node, startPos, parent)
            case SyntaxNodeKind.FuncParam => transFuncParam(node, startPos, parent)
            case SyntaxNodeKind.LambdaParam => transLambdaParam(node, startPos, parent)
            case SyntaxNodeKind.ParameterList => transParameterList(node, startPos, parent)
            case SyntaxNodeKind.ImportSpec => transImportList(node, startPos, parent)
            case SyntaxNodeKind.LineStringLiteral | SyntaxNodeKind.MultiLineStringLiteral
                | SyntaxNodeKind.MultiLineRawStringLiteral => transStringLitConstExpr(node, startPos, parent)
            case SyntaxNodeKind.RuneLiteral => transLitConstRuneExpr(node, startPos, parent)
            case SyntaxNodeKind.UnitLiteral => transUnitLitConstExpr(node, startPos, parent)
            case SyntaxNodeKind.InterpolationExpr => transInterpolationExpr(node, startPos, parent)
            case SyntaxNodeKind.BinaryExpr => transBinaryExpr(node, startPos, parent)
            case SyntaxNodeKind.IsExpr => transIsExpr(node, startPos, parent)
            case SyntaxNodeKind.AsExpr => transAsExpr(node, startPos, parent)
            case SyntaxNodeKind.ParenExpr => transParenExpr(node, startPos, parent)
            case SyntaxNodeKind.ReturnExpr => transReturnExpr(node, startPos, parent)
            case SyntaxNodeKind.IncOrDecExpr => transIncOrDecExpr(node, startPos, parent)
            case SyntaxNodeKind.RangeExpr => transRangeExpr(node, startPos, parent)
            case SyntaxNodeKind.BreakExpr => transBreakExpr(node, startPos, parent)
            case SyntaxNodeKind.ContinueExpr => transContinueExpr(node, startPos, parent)
            case SyntaxNodeKind.MemberAccess => transMemberAccess(node, startPos, parent)
            case SyntaxNodeKind.SubscriptExpr => transSubscriptExpr(node, startPos, parent)
            case SyntaxNodeKind.Lambda => transLambda(node, startPos, parent)
            case SyntaxNodeKind.ThrowExpr => transThrowExpr(node, startPos, parent)
            case SyntaxNodeKind.SynchronizedExpr => transSynchronizedExpr(node, startPos, parent)
            case SyntaxNodeKind.SpawnExpr => transSpawnExpr(node, startPos, parent)
            case SyntaxNodeKind.IfExpr => transIfExpr(node, startPos, parent)
            case SyntaxNodeKind.WhileExpr => transWhileExpr(node, startPos, parent)
            case SyntaxNodeKind.DoWhileExpr => transDoWhileExpr(node, startPos, parent)
            case SyntaxNodeKind.ForInExpr => transForInExpr(node, startPos, parent)
            case SyntaxNodeKind.TryExpr => transTryCatch(node, startPos, parent)
            case SyntaxNodeKind.QuoteExpr => transQuoteExpr(node, startPos, parent)
            case SyntaxNodeKind.QuoteTokenExpr => transQuoteTokenExpr(node, startPos, parent)
            case SyntaxNodeKind.QuoteInterpolationExpr => transQuoteInterpolationExpr(node, startPos, parent)
            case SyntaxNodeKind.TrailingClosureExpr => transTrailingClosureExpr(node, startPos, parent)
            case SyntaxNodeKind.UnsafeExpr => transUnsafeExpr(node, startPos, parent)
            // MatchExpr
            case SyntaxNodeKind.MatchExpr => transMatchExpr(node, startPos, parent)
            case SyntaxNodeKind.MatchCase => transMatchCase(node, startPos, parent)
            case SyntaxNodeKind.ConstPattern => transConstPattern(node, startPos, parent)
            case SyntaxNodeKind.WildcardPattern => transWildcardPattern(node, startPos, parent)
            case SyntaxNodeKind.VarBindingPattern => transVarBindingPattern(node, startPos, parent)
            case SyntaxNodeKind.VarOrEnumPattern => transVarOrEnumPattern(node, startPos, parent)
            case SyntaxNodeKind.TypePattern => transTypePattern(node, startPos, parent)
            case SyntaxNodeKind.EnumPattern => transEnumPattern(node, startPos, parent)
            case SyntaxNodeKind.TuplePattern => transTuplePattern(node, startPos, parent)

            case SyntaxNodeKind.TupleType => transTupleType(node, startPos, parent)
            case SyntaxNodeKind.FuncType => transFuncType(node, startPos, parent)
            case SyntaxNodeKind.VArrayType => transVArrayType(node, startPos, parent)
            case SyntaxNodeKind.ParenType => transParenType(node, startPos, parent)
            case SyntaxNodeKind.CompositeType | SyntaxNodeKind.QualifiedType | SyntaxNodeKind.RefType => transCompositeType(
                node, startPos, parent)
            case SyntaxNodeKind.OptionType => transPrefixType(node, startPos, parent)
            case SyntaxNodeKind.UnaryExpr => transUnaryExpr(node, startPos, parent)
            case SyntaxNodeKind.AssignExpr => transAssignExpr(node, startPos, parent)
            case SyntaxNodeKind.ArrayLiteral => transArrayLiteralExpr(node, startPos, parent)
            case SyntaxNodeKind.TupleLiteral => transTupleLiteralExpr(node, startPos, parent)
            case SyntaxNodeKind.RefExpr => transSymbolRef(node, startPos, parent)
            case SyntaxNodeKind.CallExpr => transCallExpr(node, startPos, parent)
            case SyntaxNodeKind.TypeConvExpr => transTypeConvExpr(node, startPos, parent)
            case SyntaxNodeKind.OptionalExpr => transOptionalExpr(node, startPos, parent)
            case SyntaxNodeKind.VArrayExpr => transVArrayExpr(node, startPos, parent)
            case SyntaxNodeKind.ImportAlias => transImportAlias(node, startPos, parent)
            case SyntaxNodeKind.ImportAll => transImportAll(node, startPos, parent)
            case SyntaxNodeKind.ImportMulti => transImportMulti(node, startPos, parent)
            case SyntaxNodeKind.ImportSingle => transImportSingle(node, startPos, parent)
            case SyntaxNodeKind.FeaturesDirective => transFeaturesDirective(node, startPos, parent)
            case SyntaxNodeKind.FeaturesSet => transFeaturesSet(node, startPos, parent)
            case SyntaxNodeKind.FeatureId => transFeatureId(node, startPos, parent)
            case _ =>
                fail("nonterminal node kind - ${node.kind.toString()} - ${node.toString()} need to be translated");
                None
        }
    }

    private func transFile(node: NonTerminal, filePath: String, parent!: ?SyntaxTreeNode = None): SourceFile {
        // BNF: (featuresDirective)? (packageSpec)? (importSpec)? decls*
        var filePathArray = filePath.split(SEPARATOR)
        var fileName = filePathArray[filePathArray.size - 1]
        let fileInfo = FileInfos(fileName, filePath)
        let beginLine: Int32 = 1
        let beginColumn: Int32 = 1
        let beginPos = CodePosition(beginLine, beginColumn, fileInfo)
        let lp = LocalParser(node.children)

        var ftrDirectivePropInfo: ?PropInfo = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.FeaturesDirective)) {
            ftrDirectivePropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)
        }
        var pkgHeaderPropInfo: ?PropInfo = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.PackageSpec)) {
            pkgHeaderPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)
        }
        let importListsPropInfo = ArrayList<PropInfo>()
        while (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ImportSpec)) {
            importListsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.moveOffset(v)
        }
        let predictDecl = {
            kind: SyntaxNodeKind => kind.isDecl()
        }
        let declsPropInfo = ArrayList<PropInfo>()
        while (lp.look(predictDecl)) {
            let declNode = lp.consume()
            declsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.moveOffset(declNode)
        }
        let propInfos = SourceFilePropInfos(importListsPropInfo.toArray(), pkgHeaderPropInfo, declsPropInfo.toArray(),
            ftrDirectivePropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        SourceFile(node, beginPos, parent, fileName, filePath, propInfos, commentsPropInfos)
    }

    private func transCommentGroup(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): CommentGroup {
        let lp = LocalParser(node.children)

        let commentsPropInfo = ArrayList<PropInfo>()
        while (lp.look(SyntaxNodeKind.Comment)) {
            let cmtNode = lp.consume()
            commentsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.moveOffset(cmtNode)
        }
        let propInfos = CommentGroupPropInfos(commentsPropInfo.toArray())
        CommentGroup(node, startPos, parent, propInfos)
    }

    private func transComment(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): Comment {
        let lp = LocalParser(node.children)

        var kind = CommentKind.Line
        var content = ""
        let tokenNode = lp.consume()
        if (let Some(tkNode) <- tokenNode && let Some(tk) <- (tkNode as TokenTerminal)) {
            content = tk.value
        }
        if (content.startsWith("/**") && content != "/**/") {
            kind = CommentKind.Document
        } else if (content.startsWith("/*")) {
            kind = CommentKind.Block
        }
        Comment(node, startPos, parent, kind, content)
    }

    private func transFeaturesDirective(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): FeaturesDirective {
        // BNF: annotationList? FEATURES NL* featuresSet end+
        var featuresKeywordPos = CodePosition()
        var ftrSetPropInfo = PropInfo()
        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(keyNode) <- lp.consume(kind: SyntaxNodeKind.FeaturesToken)) {
            featuresKeywordPos = startPos + lp.offset
            lp.moveOffset(keyNode)
        }
        if (let Some(ftrSetNode) <- lp.consume(kind: SyntaxNodeKind.FeaturesSet)) {
            ftrSetPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(ftrSetNode)
        }
        let ftrDIrectivePosInfos = FeaturesDirectivePosInfos(featuresKeywordPos)
        let ftrDirectivePropInfos = FeaturesDirectivePropInfos(ftrSetPropInfo)
        FeaturesDirective(node, startPos, parent, ftrDIrectivePosInfos, ftrDirectivePropInfos)
    }

    private func transFeaturesSet(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): FeaturesSet {
        // BNF: LCURL NL* featureId NL* (COMMA NL* featureId NL*)* RCURL
        var lCurlPos = CodePosition()
        var rCurlPos = CodePosition()
        let commaPoses = ArrayList<CodePosition>()
        let contentPropInfos = ArrayList<PropInfo>()
        let lp = LocalParser(node.children)
        if (let Some(lCurlNode) <- lp.lookAndConsume(SyntaxNodeKind.LCurlToken)) {
            lCurlPos = startPos + lp.offset
            lp.moveOffset(lCurlNode)
        }
        while (lp.look(SyntaxNodeKind.FeatureId)) {
            let node = lp.consume()
            if (let Some(ftrIdNode) <- node) {
                contentPropInfos.add(PropInfo(lp.cur - 1, lp.offset))
                lp.moveOffset(ftrIdNode)
            }
            if (let Some(commaNode) <- lp.lookAndConsume(SyntaxNodeKind.CommaToken)) {
                commaPoses.add(startPos + lp.offset)
                lp.moveOffset(commaNode)
            }
        }
        if (let Some(rCurlNode) <- lp.lookAndConsume(SyntaxNodeKind.RCurlToken)) {
            rCurlPos = startPos + lp.offset
            lp.moveOffset(rCurlNode)
        }
        let ftrSetPosInfos = FeaturesSetPosInfos(lCurlPos, rCurlPos, commaPoses.toArray())
        let ftrSetPropInfos = FeaturesSetPropInfos(contentPropInfos.toArray())
        FeaturesSet(node, startPos, parent, ftrSetPosInfos, ftrSetPropInfos)
    }

    private func transFeatureId(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): FeatureId {
        // BNF: Identifier (DOT Identifier)*
        let identValues = ArrayList<String>()
        let identifierPos = ArrayList<CodePosition>()
        let dotPoses = ArrayList<CodePosition>()
        let lp = LocalParser((node as NonTerminal).getOrThrow().children)
        while (true) {
            let ident = lp.consume()
            if (let Some(node) <- ident) {
                identValues.add(getValue(node))
                identifierPos.add(startPos + lp.offset)
                lp.moveOffset(node)
            }
            if (let Some(node) <- lp.lookAndConsume(SyntaxNodeKind.DotToken)) {
                dotPoses.add(startPos + lp.offset)
                lp.moveOffset(node)
            } else {
                break
            }
        }
        let ftrIdPosInfos = FeatureIdPosInfos(identifierPos.toArray(), dotPoses.toArray())
        return FeatureId(node, startPos, parent, identValues.toArray(), ftrIdPosInfos)
    }

    private func transPackageHeader(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): PackageHeader {
        // BNF: packageModifier? (MACRO)? PACKAGE fullPackageName
        let lp = LocalParser(node.children)

        var modifierPropInfo: ?PropInfo = None
        if (lp.look(SyntaxNodeKind.Modifier)) {
            let modifierNode = lp.consume()
            modifierPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(modifierNode?.offset)
        }
        var macroKeyWordPos: ?CodePosition = None
        var isMacroPkg: Bool = false
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.MacroToken)) {
            isMacroPkg = true
            macroKeyWordPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        let packageNode = lp.consume(kind: SyntaxNodeKind.PackageToken)
        let packageKeyWordPos = startPos + lp.offset
        lp.offset.move(packageNode?.offset)

        var packageNameIdentifiers = ArrayList<String>()
        var pkgIdentifiersPos = ArrayList<CodePosition>()
        var dotsPos = ArrayList<CodePosition>()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.PackagePrefixes)) {
            (packageNameIdentifiers, pkgIdentifiersPos, dotsPos) = transPackageName(v, startPos + lp.offset)
            lp.moveOffset(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            packageNameIdentifiers.add(getValue(v))
            pkgIdentifiersPos.add(startPos + lp.offset)
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.PackageIdentifierToken)) {
            packageNameIdentifiers.add(getValue(v))
            pkgIdentifiersPos.add(startPos + lp.offset)
            lp.offset.move(v.offset)
        }
        let propInfos = PackageHeaderPropInfos(modifierPropInfo)
        let posInfo = PackageHeaderPosInfos(macroKeyWordPos, packageKeyWordPos, pkgIdentifiersPos.toArray(),
            dotsPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        PackageHeader(node, startPos, parent, isMacroPkg, packageNameIdentifiers.toArray(), posInfo, propInfos,
            commentsPropInfos)
    }

    private func transPackageName(node: SyntaxNodeImpl, startPos: CodePosition) {
        // BNF: (IDENT .)*
        let result = ArrayList<String>()
        let identifierPos = ArrayList<CodePosition>()
        let dotsPos = ArrayList<CodePosition>()
        let lp = LocalParser((node as NonTerminal).getOrThrow().children)
        while (!lp.isEnd()) {
            let ident = lp.consume()
            if (let Some(v) <- ident) {
                result.add(getValue(v))
                identifierPos.add(startPos + lp.offset)
                lp.offset.move(v.offset)
            }
            let dotNode = lp.consume(kind: SyntaxNodeKind.DotToken)
            dotsPos.add(startPos + lp.offset)
            lp.offset.move(dotNode?.offset)
        }
        (result, identifierPos, dotsPos)
    }

    private func transAnnotation(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): Annotation {
        // BNF: AT identifier (LSQUARE Argument (COMMA Argument)* RSQUARE)?
        let lp = LocalParser(node.children)

        var opKind = AtOpKind.At
        var atOpPos = CodePosition()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AtToken)) {
            opKind = AtOpKind.At
            atOpPos = startPos + lp.offset
            lp.offset.move(v.offset)
        } else if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AtExclToken)) {
            opKind = AtOpKind.AtExcl
            atOpPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        var identifier: String = ""
        var identifierPos = CodePosition()
        if (let Some(v) <- lp.consume(kind: SyntaxNodeKind.IdentToken)) {
            identifier = getValue(v)
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }

        var argumentPropInfos = ArrayList<PropInfo>()

        var lSquarePos: ?CodePosition = None
        var rSquarePos: ?CodePosition = None
        let commasPos = ArrayList<CodePosition>()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LSquareToken)) {
            lSquarePos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        while (!lSquarePos.isNone() && !lp.look(SyntaxNodeKind.RSquareToken)) {
            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.Argument)) {
                argumentPropInfos.add(PropInfo(lp.cur - 1, lp.offset))
                lp.offset.move(v.offset)
                if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.CommaToken)) {
                    commasPos.add(startPos + lp.offset)
                    lp.offset.move(v.offset)
                }
            } else {
                // note: attr not designed
                lp.offset.move(lp.consume().getOrThrow().offset)
            }
        }
        if (lSquarePos.isSome()) {
            rSquarePos = startPos + lp.offset
        }

        let propInfo = AnnotationPropInfos(argumentPropInfos.toArray())
        let posInfo = AnnotationPosInfos(atOpPos, lSquarePos, rSquarePos, identifierPos, commasPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        Annotation(node, startPos, parent, identifier, opKind, posInfo, propInfo, commentsPropInfos)
    }

    private func transArgument(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): Argument {
        // BNF: identifier NL* COLON NL* expression
        // | expression
        // | INOUT (expression DOT)? identifier
        let lp = LocalParser(node.children)
        var identifier: ?String = None
        var isInOut: Bool = false
        var isNamed: Bool = false
        var valuePropInfo = PropInfo()
        var inoutKeyWordPos: ?CodePosition = None
        var identifierPos: ?CodePosition = None
        var colonPos: ?CodePosition = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            isNamed = true
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
            identifier = getValue(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ColonToken)) {
            colonPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.InoutToken)) {
            isInOut = true
            inoutKeyWordPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        let predictExpr = {
            kind: SyntaxNodeKind => kind.isExpr()
        }
        if (lp.look(predictExpr)) {
            let exprNode = lp.consume()
            valuePropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(exprNode?.offset)
        }

        let propInfos = ArgumentPropInfos(valuePropInfo)
        let posInfo = ArgumentPosInfos(inoutKeyWordPos, identifierPos, colonPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        Argument(node, startPos, parent, identifier, isInOut, isNamed, posInfo, propInfos, commentsPropInfos)
    }

    private func transModifier(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): Modifier {
        let lp = LocalParser(node.children)
        let kindNode = (lp.consume().getOrThrow() as Terminal).getOrThrow()
        let kind = match (kindNode.kind) {
            case SyntaxNodeKind.AbstractToken => ModifierKind.Abstract
            case SyntaxNodeKind.InternalToken => ModifierKind.Internal
            case SyntaxNodeKind.MutToken => ModifierKind.Mut
            case SyntaxNodeKind.OpenToken => ModifierKind.Open
            case SyntaxNodeKind.OperatorToken => ModifierKind.Operator
            case SyntaxNodeKind.OverrideToken => ModifierKind.Override
            case SyntaxNodeKind.PrivateToken => ModifierKind.Private
            case SyntaxNodeKind.ProtectedToken => ModifierKind.Protected
            case SyntaxNodeKind.PublicToken => ModifierKind.Public
            case SyntaxNodeKind.RedefToken => ModifierKind.Redef
            case SyntaxNodeKind.SealedToken => ModifierKind.Sealed
            case SyntaxNodeKind.StaticToken => ModifierKind.Static
            case SyntaxNodeKind.UnsafeToken => ModifierKind.Unsafe
            case SyntaxNodeKind.ConstToken => ModifierKind.Const
            case _ => throw Exception("ParseException: This SyntaxNode kind is not modifier.")
        }
        lp.moveOffset(kindNode)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        Modifier(node, startPos, parent, kind, commentsPropInfos)
    }

    private func transBlock(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): Block {
        // BNF: LCurl (expressionOrDeclarations)* RCurl
        let lp = LocalParser(node.children)

        let lCurlNode = lp.consume(kind: SyntaxNodeKind.LCurlToken)
        let lCurlPos = startPos + lp.offset
        lp.offset.move(lCurlNode?.offset)

        var nodesPropInfo: ?PropInfo = None
        var foundNodes = false
        while (!lp.look(SyntaxNodeKind.RCurlToken)) {
            let curNode = lp.consume()
            if (!foundNodes) {
                nodesPropInfo = PropInfo(lp.cur - 1, lp.offset)
                foundNodes = true
            }
            lp.offset.move(curNode?.offset)
        }

        let rCurlNode = lp.consume(kind: SyntaxNodeKind.RCurlToken)
        let rCurlPos = startPos + lp.offset
        lp.offset.move(rCurlNode?.offset)

        let propInfos = BlockPropInfos(nodesPropInfo)
        let posInfos = BlockPosInfos(lCurlPos, rCurlPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        Block(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transLambdaParam(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): LambdaParam {
        // BNF: identifier (COLON type)?
        let lp = LocalParser(node.children)

        var identifierPos: CodePosition = CodePosition()
        var name = ""
        if (let Some(v) <- lp.consume(kind: SyntaxNodeKind.IdentToken)) {
            name = getValue(v)
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        var colonPos: ?CodePosition = None
        var typeAnnotationPropInfo: ?PropInfo = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ColonToken)) {
            colonPos = startPos + lp.offset
            lp.offset.move(v.offset)
            let tyAnnoNode = lp.consume()
            typeAnnotationPropInfo = PropInfo(lp.cur - 1, lp.offset)
        }

        let propInfo = LambdaParamPropInfos(typeAnnotationPropInfo)
        let posInfo = LambdaParamPosInfos(identifierPos, colonPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        LambdaParam(node, startPos, parent, name, posInfo, propInfo, commentsPropInfos)
    }

    private func transFuncParam(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): FuncParam {
        // BNF: (anno)* (modifier)* identifier Not? COLON type (ASSIGN expr)
        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }

        var kind: ?VarKind = None
        var varKindKeyWordPos: ?CodePosition = None
        if (let Some(v) <- lp.look([SyntaxNodeKind.VarToken, SyntaxNodeKind.LetToken])) {
            if (let Some(node) <- lp.consume(kind: v)) {
                kind = transVarKind(node)
                varKindKeyWordPos = startPos + lp.offset
                lp.offset.move(node.offset)
            }
        }

        var identifierPos: CodePosition = CodePosition()
        var name = ""
        if (let Some(v) <- lp.consume(kind: SyntaxNodeKind.IdentToken)) {
            name = getValue(v)
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }

        var notPos: ?CodePosition = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.NotToken)) {
            notPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        let colonNode = lp.consume(kind: SyntaxNodeKind.ColonToken)
        let colonPos = startPos + lp.offset
        lp.offset.move(colonNode?.offset)

        let typeAnnoNode = lp.consume()
        let tyAnnoPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(typeAnnoNode?.offset)

        var assignPos: ?CodePosition = None
        var defaultValuePropInfo: ?PropInfo = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AssignToken)) {
            assignPos = startPos + lp.offset
            lp.offset.move(v.offset)
            let valueNode = lp.consume()
            defaultValuePropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(valueNode?.offset)
        }

        let propInfo = FuncParamPropInfos(defaultValuePropInfo, tyAnnoPropInfo)
        let posInfo = FuncParamPosInfos(varKindKeyWordPos, identifierPos, colonPos, assignPos, notPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        FuncParam(node, startPos, parent, kind, name, posInfo, propInfo, commentsPropInfos)
    }

    private func transParameterList(node: SyntaxNodeImpl, startPos: CodePosition, parent: ?SyntaxTreeNode): ParameterList {
        // BNF :LPAREN? FuncParam (Comma FuncParam)* RPAREN?
        // BNF :LPAREN? RPAREN?
        let lp = LocalParser(((node as NonTerminal).getOrThrow()).children)
        var lParenPos: ?CodePosition = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            lParenPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        let paramsPropInfo = ArrayList<PropInfo>()
        let commasPos = ArrayList<CodePosition>()
        while (!lp.look([SyntaxNodeKind.FuncParam, SyntaxNodeKind.MacroExpandParam, SyntaxNodeKind.LambdaParam])
                .isNone()) {
            let paramNode = lp.consume()
            paramsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.offset.move(paramNode?.offset)

            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.CommaToken)) {
                commasPos.add(startPos + lp.offset)
                lp.offset.move(v.offset)
            }
        }
        var rParenPos: ?CodePosition = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RParenToken)) {
            rParenPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        let propInfo = ParameterListPropInfos(paramsPropInfo.toArray())
        let posInfo = ParameterListPosInfos(lParenPos, commasPos.toArray(), rParenPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ParameterList(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transImportAll(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ImportAll {
        // BNF: (packageNameIdentifier NL* DOT NL*)+ MUL
        let lp = LocalParser(node.children)
        let (idents, identPos, dotsPos) = lp.consumeListOfPos(startPos, kind: SyntaxNodeKind.IdentToken,
            sep: SyntaxNodeKind.DotToken)
        lp.consume()
        let prefixes = idents |> map({ident: SyntaxNodeImpl => getValue(ident)}) |> collectArray<String>
        let mulPos = startPos + lp.offset
        let posInfo = ImportAllPosInfos(mulPos)
        let posInfo1 = ImportContentPosInfos(dotsPos.toArray(), identPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ImportAll(node, startPos, parent, posInfo, posInfo1, prefixes, commentsPropInfos)
    }

    private func transImportSingle(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ImportSingle {
        // BNF: (packageNameIdentifier NL* DOT NL*)* (identifier | packageNameIdentifier)
        let lp = LocalParser(node.children)
        let (idents, identPos, dotsPos) = lp.consumeListOfPos(startPos, kind: SyntaxNodeKind.IdentToken,
            sep: SyntaxNodeKind.DotToken)
        let prefixes = idents |> map({ident: SyntaxNodeImpl => getValue(ident)}) |> collectArray<String>
        let sz = identPos.size
        let posInfo = ImportSinglePosInfos(identPos[sz - 1])
        let posInfo1 = ImportContentPosInfos(dotsPos.toArray(), identPos.toArray()[..sz - 1])
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ImportSingle(node, startPos, parent, posInfo, posInfo1, prefixes[sz - 1], prefixes[..sz - 1], commentsPropInfos)
    }

    private func transImportAlias(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ImportAlias {
        // BNF: importSingle NL* AS NL* identifier
        let lp = LocalParser(node.children)
        let (idents, identPos, dotsPos) = lp.consumeListOfPos(startPos, kind: SyntaxNodeKind.IdentToken,
            sep: SyntaxNodeKind.DotToken)
        let prefixes = idents |> map({ident: SyntaxNodeImpl => getValue(ident)}) |> collectArray<String>
        var curNode = lp.consume()
        let asPos = startPos + lp.offset
        lp.offset.move(curNode?.offset)
        let aliasName = lp.consume(kind: SyntaxNodeKind.IdentToken).getOrThrow()
        let aliasNamePos = startPos + lp.offset
        let sz = identPos.size
        let posInfo = ImportAliasPosInfos(identPos[sz - 1], asPos, aliasNamePos)
        let posInfo1 = ImportContentPosInfos(dotsPos.toArray(), identPos.toArray()[..sz - 1])
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ImportAlias(node, startPos, parent, posInfo, posInfo1, getValue(aliasName), prefixes[sz - 1], prefixes[..sz - 1],
            commentsPropInfos)
    }

    private func transImportMulti(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ImportMulti {
        // BNF: (packageNameIdentifier NL* DOT NL*)* LCURL NL*
        // (importSingle | importAlias | importAll) NL*
        // (COMMA NL* (importSingle | importAlias | importAll))* NL*
        // COMMA? NL* RCURL
        let lp = LocalParser(node.children)
        let importContents = ArrayList<ImportContent>()
        let (idents, identPos, dotsPos) = lp.consumeListOfPos(startPos, kind: SyntaxNodeKind.IdentToken,
            sep: SyntaxNodeKind.DotToken)
        let prefixes = idents |> map({ident: SyntaxNodeImpl => getValue(ident)}) |> collectArray<String>
        let commasPos = ArrayList<CodePosition>()
        var (lCurlPos, rCurlPos) = (CodePosition(), CodePosition())
        var findImportContent = false
        var contentsPropInfo = PropInfo()
        while (let Some(node) <- lp.consume()) {
            match (node) {
                case nt: NonTerminal =>
                    if (!findImportContent) {
                        findImportContent = true
                        contentsPropInfo = PropInfo(lp.cur - 1, lp.offset)
                    }
                case _ => match (node.kind) {
                    case SyntaxNodeKind.CommaToken => commasPos.add(startPos + lp.offset)
                    case SyntaxNodeKind.LCurlToken => lCurlPos = startPos + lp.offset
                    case SyntaxNodeKind.RCurlToken =>
                        rCurlPos = startPos + lp.offset
                        break
                    case _ => fail("unExpected node kind in importMulti")
                }
            }
            lp.offset.move(node.offset)
        }
        let posInfo = ImportMultiPosInfos(lCurlPos, rCurlPos, commasPos.toArray())
        let posInfo1 = ImportContentPosInfos(dotsPos.toArray(), identPos.toArray())
        let propInfo = ImportMultiPropInfos(contentsPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ImportMulti(node, startPos, parent, posInfo, propInfo, posInfo1, prefixes, commentsPropInfos)
    }

    private func transImportList(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ImportList {
        // BNF: importModifier? NL* IMPORT NL* importContent end+
        let lp = LocalParser(node.children)
        var modifier: ?Modifier = None
        var content: ?ImportContent = None
        var kind = ImportKind.Alias
        var contentsPropInfo = PropInfo()
        var modifierPropInfo: ?PropInfo = None
        if (lp.look(SyntaxNodeKind.Modifier)) {
            let curNode = lp.consume()
            modifierPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(curNode)
        }
        let importNode = lp.consume(kind: SyntaxNodeKind.ImportToken)
        let importKeyWordPos = startPos + lp.offset
        lp.moveOffset(importNode)
        if (let Some(importContent) <- lp.consume()) {
            contentsPropInfo = PropInfo(lp.cur - 1, lp.offset)
            kind = match (importContent.kind) {
                case SyntaxNodeKind.ImportAlias => ImportKind.Alias
                case SyntaxNodeKind.ImportAll => ImportKind.All
                case SyntaxNodeKind.ImportMulti => ImportKind.Multi
                case SyntaxNodeKind.ImportSingle => ImportKind.Single
                case _ =>
                    fail("translate ImportList error, not find import content")
                    kind
            }
        }
        let posInfo = ImportListPosInfos(importKeyWordPos)
        let propInfo = ImportListPropInfos(contentsPropInfo, modifierPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ImportList(node, startPos, None, posInfo, propInfo, kind, commentsPropInfos)
    }

    private func transGenericConstraint(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): GenericConstraint {
        // BNF type UPPERBOUND type (BITAND type)*
        let lp = LocalParser(node.children)

        let typeAnnoNode = lp.consume()
        let tyAnnoPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(typeAnnoNode?.offset)
        let upperNode = lp.consume(kind: SyntaxNodeKind.UpperBoundToken)
        let upBoundPos = startPos + lp.offset
        lp.offset.move(upperNode?.offset)
        let upperBoundsPropInfos = ArrayList<PropInfo>()
        let bitEndPos = ArrayList<CodePosition>()
        while (!lp.isEnd()) {
            if (let Some(node) <- lp.tryConsume()) {
                upperBoundsPropInfos.add(PropInfo(lp.cur - 1, lp.offset))
                lp.offset.move(node.offset)
            }

            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.BitAndToken)) {
                bitEndPos.add(startPos + lp.offset)
                lp.offset.move(v.offset)
            }
        }

        let propInfo = GenericConstraintPropInfos(tyAnnoPropInfo, upperBoundsPropInfos.toArray())
        let posInfo = GenericConstraintPosInfos(upBoundPos, bitEndPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        GenericConstraint(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transGenericConstraints(node: SyntaxNodeImpl, startPos: CodePosition, parent: ?SyntaxTreeNode): GenericConstraints {
        // BNF: WHERE constraints (, constraints)*
        let lp = LocalParser((node as NonTerminal).getOrThrow().children)
        let whereNode = lp.consume(kind: SyntaxNodeKind.WhereToken)
        let wherePos = startPos + lp.offset
        lp.offset.move(whereNode?.offset)
        let commasPos = ArrayList<CodePosition>()
        let constraintsPropInfos = ArrayList<PropInfo>()
        while (!lp.isEnd()) {
            let constraintNode = lp.consume(kind: SyntaxNodeKind.GenericConstraint)
            if (constraintNode.isSome()) {
                constraintsPropInfos.add(PropInfo(lp.cur - 1, lp.offset))
                lp.offset.move(constraintNode?.offset)
            }

            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.CommaToken)) {
                commasPos.add(startPos + lp.offset)
                lp.offset.move(v.offset)
            }
        }

        let propInfo = GenericConstraintsPropInfos(constraintsPropInfos.toArray())
        let posInfo = GenericConstraintsPosInfos(wherePos, commasPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        GenericConstraints(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    // Decl

    private func transGenericParam(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): GenericParam {
        let lp = LocalParser(node.children)
        var name: String = ""

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            name = getValue(v)
            lp.moveOffset(v)
        }

        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        GenericParam(node, startPos, parent, name, commentsPropInfos)
    }

    private func transFuncDeclModifierList(startPos: CodePosition, modifierList: NonTerminal, lp: LocalParser) {
        var operatorFlag = false
        var kind = FuncKind.PrimaryConstructor
        var funcKindKeyWordPos: Option<CodePosition> = None
        let mlp = LocalParser(modifierList.children)
        while (let Some(modifier) <- mlp.tryConsume()) {
            let mp = LocalParser((modifier as NonTerminal).getOrThrow().children)
            while (let Some(vm) <- mp.tryConsume()) {
                match (vm.kind) {
                    case SyntaxNodeKind.ForeignToken =>
                        kind = FuncKind.Foreign
                        funcKindKeyWordPos = startPos + lp.offset + mlp.offset + mp.offset
                        mp.offset.move(vm.offset)
                    case SyntaxNodeKind.OperatorToken =>
                        operatorFlag = true
                        mp.offset.move(vm.offset)
                    case _ => mp.offset.move(vm.offset)
                }
            }
            mlp.moveOffset(modifier)
        }
        return (kind, funcKindKeyWordPos, operatorFlag)
    }

    private func consumeParamsAndRetTyAnnotation(lp: LocalParser, startPos: CodePosition) {
        var paramNode = lp.consume()
        let paramsPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(paramNode?.offset)

        var retTyAnnotationPropInfo: Option<PropInfo> = None
        var retTyAnnotationColonPos: Option<CodePosition> = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ColonToken)) {
            retTyAnnotationColonPos = startPos + lp.offset
            lp.offset.move(v.offset)

            var tyAnnoNode = lp.consume()
            retTyAnnotationPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(tyAnnoNode?.offset)
        }

        return (paramsPropInfo, retTyAnnotationPropInfo, retTyAnnotationColonPos)
    }

    private func consumeNamedGenericDeclHead(lp: LocalParser, startPos: CodePosition) {
        var (name, identifierPos) = ("", CodePosition())
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            name = getValue(v)
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }

        var genericParamsPropInfo: ?PropInfo = None
        var genericParamsCommasPos = Array<CodePosition>()
        var (genericParamsLAnglePos, genericParamsRAnglePos): (Option<CodePosition>, Option<CodePosition>) = (None, None)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.TypeArguments)) {
            genericParamsPropInfo = PropInfo(lp.cur - 1, lp.offset)
            (genericParamsLAnglePos, genericParamsCommasPos, genericParamsRAnglePos) = transGenericParams(v,
                startPos + lp.offset)
            lp.moveOffset(v)
        }

        return (name, identifierPos, genericParamsPropInfo, genericParamsLAnglePos, genericParamsCommasPos,
            genericParamsRAnglePos)
    }

    private func consumeUpperBoundSuperTypes(lp: LocalParser, startPos: CodePosition) {
        var upperBoundPos: Option<CodePosition> = None
        var (superTyAnnotationsPropInfo, superTyAnnotationsBitAndPos) = (ArrayList<PropInfo>(), ArrayList<CodePosition>())
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.UpperBoundToken)) {
            upperBoundPos = startPos + lp.offset
            lp.offset.move(v.offset)
            let predictTypeAnnotation = {
                kind: SyntaxNodeKind => kind.isTypeAnnotation()
            }
            while (lp.look(predictTypeAnnotation)) {
                let tyAnnoNode = lp.consume()
                superTyAnnotationsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
                lp.offset.move(tyAnnoNode?.offset)

                if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.BitAndToken)) {
                    superTyAnnotationsBitAndPos.add(startPos + lp.offset)
                    lp.moveOffset(v)
                }
            }
        }

        return (upperBoundPos, superTyAnnotationsPropInfo, superTyAnnotationsBitAndPos)
    }

    private func consumeGenericConstraintsPropInfo(lp: LocalParser): ?PropInfo {
        var genericConstraintsPropInfo: ?PropInfo = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.GenericConstraints)) {
            genericConstraintsPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(v.offset)
        }
        return genericConstraintsPropInfo
    }

    private func consumeBodyNodeAndPropInfo(lp: LocalParser) {
        let bodyNode = lp.consume(kind: SyntaxNodeKind.Body)
        let bodyPropInfo = PropInfo(lp.cur - 1, lp.offset)
        return (bodyNode, bodyPropInfo)
    }

    private func consumeMacroExpandCommon(lp: LocalParser, startPos: CodePosition) {
        var (calleePropInfo, macroAttrsPropInfo, macroInputsPropInfo) = (PropInfo(), PropInfo(), PropInfo())
        var (lSquarePos, rSquarePos): (?CodePosition, ?CodePosition) = (None, None)
        var (lParenPos, rParenPos): (?CodePosition, ?CodePosition) = (None, None)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }
        let atNode = lp.consume(kind: SyntaxNodeKind.AtToken)
        let atPos = startPos + lp.offset
        lp.moveOffset(atNode)

        let calleeNode = lp.consume()
        calleePropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(calleeNode)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LSquareToken)) {
            lSquarePos = startPos + lp.offset
            lp.moveOffset(v)
        }
        if (lp.look(SyntaxNodeKind.TokenList) && let Some(v) <- lp.consume()) {
            macroAttrsPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RSquareToken)) {
            rSquarePos = startPos + lp.offset
            lp.moveOffset(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            lParenPos = startPos + lp.offset
            lp.moveOffset(v)
            if (lp.look(SyntaxNodeKind.TokenList) && let Some(v) <- lp.consume()) {
                macroInputsPropInfo = PropInfo(lp.cur - 1, lp.offset)
                lp.moveOffset(v)
            }
            let rParenNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
            rParenPos = startPos + lp.offset
            lp.moveOffset(rParenNode)
        } else {
            let inputNode = lp.consume()
            macroInputsPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(inputNode)
        }

        return (calleePropInfo, macroAttrsPropInfo, macroInputsPropInfo, atPos, lSquarePos, rSquarePos, lParenPos,
            rParenPos)
    }

    private func consumeSeparatedElementsStartPropInfo(lp: LocalParser, startPos: CodePosition,
        shouldConsumeElement: (SyntaxNodeKind) -> Bool, sep: SyntaxNodeKind) {
        let commasPos = ArrayList<CodePosition>()
        var findStart = false
        var elementsPropInfo = PropInfo()
        while (lp.look(shouldConsumeElement)) {
            if (let Some(node) <- lp.consume()) {
                if (!findStart) {
                    findStart = true
                    elementsPropInfo = PropInfo(lp.cur - 1, lp.offset)
                }
                lp.moveOffset(node)
            }
            if (lp.look(sep)) {
                let curSep = lp.consume(kind: sep)
                commasPos.add(startPos + lp.offset)
                lp.moveOffset(curSep)
            }
        }
        return (elementsPropInfo, commasPos)
    }

    private func consumeParenConditionPropInfo(lp: LocalParser, startPos: CodePosition) {
        var curNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
        let lParenPos = startPos + lp.offset
        lp.moveOffset(curNode)

        curNode = lp.consume()
        let conditionPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)

        curNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        let rParenPos = startPos + lp.offset
        lp.moveOffset(curNode)

        return (conditionPropInfo, lParenPos, rParenPos)
    }

    private func consumeCommaPos(lp: LocalParser, startPos: CodePosition) {
        let curNode = lp.consume(kind: SyntaxNodeKind.CommaToken)
        let pos = startPos + lp.offset
        lp.moveOffset(curNode)
        return pos
    }

    private func consumeOptionalTypeLabel(lp: LocalParser, startPos: CodePosition, labels: ArrayList<String>,
        labelsPos: ArrayList<CodePosition>, colonPos: ArrayList<CodePosition>) {
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            labels.add(getValue(v))
            labelsPos.add(startPos + lp.offset)
            lp.moveOffset(v)

            let curNode = lp.consume(kind: SyntaxNodeKind.ColonToken)
            colonPos.add(startPos + lp.offset)
            lp.moveOffset(curNode)
        }
    }

    private func transFuncDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): FuncDecl {
        // BNF: anno? modifier? (FUNC? IDENT | INIT | ~INIT)  typeParams? funcParams (COLON type)? genericConstraints? block?
        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        var operatorFlag = false
        var kind = FuncKind.PrimaryConstructor
        var funcKindKeyWordPos: Option<CodePosition> = None
        // check if foreign func
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList) && let Some(modifierList) <- (v as NonTerminal)) {
            (kind, funcKindKeyWordPos, operatorFlag) = transFuncDeclModifierList(startPos, modifierList, lp)
            lp.offset.move(v.offset)
        }
        var funcKeyWordPos: Option<CodePosition> = None
        var identifierPos: CodePosition = CodePosition()
        var name = ""
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            name = getValue(v)
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
        } else if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.FuncToken)) {
            kind = match (kind) {
                case Foreign => FuncKind.Foreign
                case _ => FuncKind.Normal
            }
            funcKeyWordPos = startPos + lp.offset
            lp.offset.move(v.offset)
            if (let Some(ident) <- lp.consume(kind: SyntaxNodeKind.IdentToken)) {
                name = getValue(ident)
                identifierPos = startPos + lp.offset
                lp.offset.move(ident.offset)
            }
        } else if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.BitNotToken)) {
            name = "~init"
            funcKindKeyWordPos = startPos + lp.offset
            lp.offset.move(v.offset)
            kind = FuncKind.Finalizer
            if (let Some(ident) <- lp.consume(kind: SyntaxNodeKind.InitToken)) {
                identifierPos = startPos + lp.offset
                lp.offset.move(ident.offset)
            }
        } else if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.InitToken)) {
            name = "init"
            kind = FuncKind.Constructor
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        var genericParamsPropInfo: ?PropInfo = None
        var genericParamsLAnglePos: Option<CodePosition> = None
        var genericParamsCommasPos: Array<CodePosition> = Array<CodePosition>()
        var genericParamsRAnglePos: Option<CodePosition> = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.TypeArguments)) {
            genericParamsPropInfo = PropInfo(lp.cur - 1, lp.offset)
            (genericParamsLAnglePos, genericParamsCommasPos, genericParamsRAnglePos) = transGenericParams(v,
                startPos + lp.offset)
            lp.moveOffset(v)
        }
        let paramsNode = lp.consume(kind: SyntaxNodeKind.ParameterList)
        let paramsPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(paramsNode?.offset)

        var retTyAnnotationPropInfo: ?PropInfo = None
        var retTyAnnotationColonPos: Option<CodePosition> = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ColonToken)) {
            retTyAnnotationColonPos = startPos + lp.offset
            lp.offset.move(v.offset)
            let annoNode = lp.consume()
            retTyAnnotationPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(annoNode?.offset)
        }

        var genericConstraintsPropInfo: ?PropInfo = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.GenericConstraints)) {
            genericConstraintsPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(v.offset)
        }

        var bodyPropInfo: ?PropInfo = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.Block)) {
            bodyPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(v.offset)
        }

        if (operatorFlag) {
            kind = FuncKind.Operator
        }

        let propInfo = FuncDeclPropInfos(bodyPropInfo, genericConstraintsPropInfo, genericParamsPropInfo, paramsPropInfo,
            retTyAnnotationPropInfo)
        let posInfo = FuncDeclPosInfos(funcKindKeyWordPos, funcKeyWordPos, identifierPos, genericParamsLAnglePos,
            genericParamsCommasPos, genericParamsRAnglePos, retTyAnnotationColonPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        FuncDecl(node, startPos, parent, name, kind, posInfo, propInfo, commentsPropInfos)
    }

    private func transVarKind(node: SyntaxNodeImpl): VarKind {
        return match (node.kind) {
            case SyntaxNodeKind.VarToken => VarKind.Var
            case SyntaxNodeKind.LetToken => VarKind.Let
            case SyntaxNodeKind.ConstToken => VarKind.Const
            case _ => throw Exception("ParseException: This SyntaxNode kind is not var kind.")
        }
    }

    // var a = 1
    private func transVarDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): VarDecl {
        // BNF: annotationList? variableModifier* NL* (LET | VAR) NL* patternsMaybeIrrefutable ( (NL* COLON NL* type)? (NL* ASSIGN NL* expression)
        //    | (NL* COLON NL* type)
        let lp = LocalParser(node.children)
        var initializerPropInfo: ?PropInfo = None
        var patternPropInfo = PropInfo()
        var tyAnnoPropInfo: ?PropInfo = None

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        var modifiers = Array<Modifier>()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }
        var (kind, varKindKeyWordPos) = (VarKind.Const, CodePosition())
        var (identifierPos, tyAnnotationColonPos, assignPos): (?CodePosition, ?CodePosition, ?CodePosition) = (None, None,
            None)
        if (let Some(v) <- lp.look([SyntaxNodeKind.VarToken, SyntaxNodeKind.LetToken, SyntaxNodeKind.ConstToken])) {
            if (let Some(node) <- lp.consume(kind: v)) {
                kind = transVarKind(node)
                varKindKeyWordPos = startPos + lp.offset
                lp.offset.move(node.offset)
            }
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.VarBindingPattern)) {
            identifierPos = startPos + lp.offset
            patternPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(v.offset)
        } else if (let Some(v) <- lp.look([SyntaxNodeKind.WildcardPattern, SyntaxNodeKind.TuplePattern])) {
            let patternNode = lp.consume(kind: v)
            patternPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(patternNode?.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ColonToken)) {
            tyAnnotationColonPos = startPos + lp.offset
            lp.offset.move(v.offset)
            let annoNode = lp.consume()
            tyAnnoPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(annoNode?.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AssignToken)) {
            assignPos = startPos + lp.offset
            lp.offset.move(v.offset)
            let initNode = lp.consume()
            initializerPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(initNode?.offset)
        }

        let propInfos = VarDeclPropInfos(initializerPropInfo, patternPropInfo, tyAnnoPropInfo)
        let posInfos = VarDeclPosInfos(varKindKeyWordPos, identifierPos, tyAnnotationColonPos, assignPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        VarDecl(node, startPos, parent, kind, posInfos, propInfos, commentsPropInfos)
    }

    private func transMainDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): MainDecl {
        // BNF: MAIN funcParams (COLON type)? Block
        let lp = LocalParser(node.children)
        var mainKeyWordPos: CodePosition = CodePosition()
        if (let Some(v) <- lp.consume(kind: SyntaxNodeKind.MainToken)) {
            mainKeyWordPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }

        let (paramsPropInfo, retTyAnnotationPropInfo, retTyAnnotationColonPos) = consumeParamsAndRetTyAnnotation(lp,
            startPos)

        var blockNode = lp.consume()
        let blockPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(blockNode?.offset)

        let propInfos = MainDeclPropInfos(blockPropInfo, paramsPropInfo, retTyAnnotationPropInfo)
        let posInfo = MainDeclPosInfos(mainKeyWordPos, retTyAnnotationColonPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        MainDecl(node, startPos, parent, posInfo, propInfos, commentsPropInfos)
    }

    private func transTypeAlias(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): TypeAlias {
        // BNF: annotationList? (typeModifier NL*)? TYPE_ALIAS NL* identifier (NL* typeParameters)? NL* ASSIGN NL* type end*
        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }
        var aliasName: String = ""
        var typeParametersPropInfo: ?PropInfo = None
        var typeAliasKeyWordPos = CodePosition()
        var identifierPos = CodePosition()
        var assignPos = CodePosition()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.TypeToken)) {
            typeAliasKeyWordPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
            aliasName = getValue(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.TypeArguments)) {
            typeParametersPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AssignToken)) {
            assignPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        let tyAnnoNode = lp.consume()
        let tyAnnoPropInfo = PropInfo(lp.cur - 1, lp.offset)

        let propInfo = TypeAliasPropInfos(tyAnnoPropInfo, typeParametersPropInfo)
        let posInfo = TypeAliasPosInfos(typeAliasKeyWordPos, identifierPos, assignPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        TypeAlias(node, startPos, parent, aliasName, posInfo, propInfo, commentsPropInfos)
    }

    private func transBody(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): Body {
        var memberDeclsPropInfo = ArrayList<PropInfo>()
        let bitOrsPos = ArrayList<CodePosition>()
        var ellipsisPos: ?CodePosition = None
        let lp = LocalParser(node.children)
        let lCurlNode = lp.consume(kind: SyntaxNodeKind.LCurlToken)
        let lCurlPos = startPos + lp.offset
        lp.offset.move(lCurlNode?.offset)

        let predictDecl = {
            kind: SyntaxNodeKind => kind.isDecl()
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.BitOrToken)) {
            bitOrsPos.add(startPos + lp.offset)
            lp.offset.move(v.offset)
        }
        while (lp.look(predictDecl)) {
            let declNode = lp.consume()
            memberDeclsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.offset.move(declNode?.offset)

            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.BitOrToken)) {
                bitOrsPos.add(startPos + lp.offset)
                lp.offset.move(v.offset)
            }
            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.EllipsisToken)) {
                ellipsisPos = startPos + lp.offset
                lp.offset.move(v.offset)
            }
        }

        let rCurlNode = lp.consume(kind: SyntaxNodeKind.RCurlToken)
        let rCurlPos = startPos + lp.offset
        lp.offset.move(rCurlNode?.offset)

        let propInfos = BodyPropInfos(memberDeclsPropInfo.toArray())
        let enumBodyPosInfos = EnumBodyPosInfos(bitOrsPos.toArray(), ellipsisPos)
        let posInfos = BodyPosInfos(lCurlPos, rCurlPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        Body(node, startPos, parent, posInfos, enumBodyPosInfos, propInfos, commentsPropInfos)
    }

    private func transClassDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ClassDecl {
        // BNF: annotationList? (classModifierList NL*)? CLASS NL* identifier (NL* typeParameters NL*)? (NL* UPPERBOUND NL* superClassOrInterfaces)? (NL* genericConstraints)? (NL* classBody)
        let lp = LocalParser(node.children)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }

        let classNode = lp.consume(kind: SyntaxNodeKind.ClassToken)
        let classKeyWordPos = startPos + lp.offset
        lp.offset.move(classNode?.offset)

        let (name, identifierPos, genericParamsPropInfo, genericParamsLAnglePos, genericParamsCommasPos,
            genericParamsRAnglePos) = consumeNamedGenericDeclHead(lp, startPos)
        let (upperBoundPos, superTyAnnotationsPropInfo, superTyAnnotationsBitAndPos) = consumeUpperBoundSuperTypes(lp,
            startPos)
        let genericConstraintsPropInfo = consumeGenericConstraintsPropInfo(lp)
        let (bodyNode, bodyPropInfo) = consumeBodyNodeAndPropInfo(lp)
        lp.offset.move(bodyNode?.offset)

        let propInfos = ClassDeclPropInfos(bodyPropInfo, genericConstraintsPropInfo, genericParamsPropInfo,
            superTyAnnotationsPropInfo.toArray())
        let posInfos = ClassDeclPosInfos(classKeyWordPos, identifierPos, genericParamsLAnglePos, genericParamsCommasPos,
            genericParamsRAnglePos, upperBoundPos, superTyAnnotationsBitAndPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ClassDecl(node, startPos, parent, name, posInfos, propInfos, commentsPropInfos)
    }

    private func transPropGetterOrSetter(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): PropGetterOrSetter {
        // BNF: GET NL* LPAREN RPAREN NL* block end* 
        //    | annotationList? SET NL* LPAREN identifier RPAREN NL* block end*
        let lp = LocalParser(node.children)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }

        var identifier: ?String = None
        var isGetter: Bool = false
        var setKeyWordPos: ?CodePosition = None
        var getKeyWordPos: ?CodePosition = None
        var identifierPos: ?CodePosition = None
        var lParenPos: CodePosition = CodePosition()
        var rParenPos: CodePosition = CodePosition()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.SetToken)) {
            setKeyWordPos = startPos + lp.offset
            lp.offset.move(v.offset)

            let lParenNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
            lParenPos = startPos + lp.offset
            lp.offset.move(lParenNode?.offset)
            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
                identifier = getValue(v)
                identifierPos = startPos + lp.offset
                lp.offset.move(v.offset)
            }
            let rParenNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
            rParenPos = startPos + lp.offset
            lp.offset.move(rParenNode?.offset)
        } else if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.GetToken)) {
            isGetter = true
            getKeyWordPos = startPos + lp.offset
            lp.offset.move(v.offset)
            let lParenNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
            lParenPos = startPos + lp.offset
            lp.offset.move(lParenNode?.offset)

            let rParenNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
            rParenPos = startPos + lp.offset
            lp.offset.move(rParenNode?.offset)
        }

        var blockPropInfo = PropInfo()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.Block)) {
            blockPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(v.offset)
        }

        let propInfos = PropGetterOrSetterPropInfos(blockPropInfo)
        let posInfos = PropGetterOrSetterPosInfos(getKeyWordPos, setKeyWordPos, identifierPos, lParenPos, rParenPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        PropGetterOrSetter(node, startPos, parent, identifier, isGetter, posInfos, propInfos, commentsPropInfos)
    }

    private func transPropDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): PropDecl {
        // BNF: annotationList? propertyModifier* NL* PROP NL* identifier NL* propertyTypeAndBody
        let lp = LocalParser(node.children)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }

        let propNode = lp.consume(kind: SyntaxNodeKind.PropToken)
        let propKeyWordPos = startPos + lp.offset
        lp.offset.move(propNode?.offset)

        var name: String = ""
        var identifierPos: CodePosition = CodePosition()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            name = getValue(v)
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }

        var tyAnnotationColonPos: CodePosition = CodePosition()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ColonToken)) {
            tyAnnotationColonPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }

        let predictTypeAnnotation = {
            kind: SyntaxNodeKind => kind.isTypeAnnotation()
        }

        var tyAnnotationPropInfo = PropInfo()
        if (lp.look(predictTypeAnnotation)) {
            let tyAnnoNode = lp.consume()
            tyAnnotationPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(tyAnnoNode?.offset)
        }

        var lCurlPos: ?CodePosition = None
        var rCurlPos: ?CodePosition = None
        var getterPropInfo: ?PropInfo = None
        var setterPropInfo: ?PropInfo = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LCurlToken)) {
            lCurlPos = startPos + lp.offset
            lp.offset.move(v.offset)
            while (lp.look(SyntaxNodeKind.PropGetterOrSetter)) {
                let node = lp.consume()
                var cur = cast<PropGetterOrSetter>(transNode(node.getOrThrow(), startPos + lp.offset, parent))
                if (let Some(ty) <- cur && ty.isGetter) {
                    getterPropInfo = PropInfo(lp.cur - 1, lp.offset)
                } else {
                    setterPropInfo = PropInfo(lp.cur - 1, lp.offset)
                }
                lp.offset.move(node?.offset)
            }
            let rCurlNode = lp.consume(kind: SyntaxNodeKind.RCurlToken)
            rCurlPos = startPos + lp.offset
            lp.offset.move(rCurlNode?.offset)
        }
        let propInfos = PropDeclPropInfos(getterPropInfo, setterPropInfo, tyAnnotationPropInfo)
        let posInfos = PropDeclPosInfos(propKeyWordPos, identifierPos, tyAnnotationColonPos, lCurlPos, rCurlPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        PropDecl(node, startPos, parent, name, posInfos, propInfos, commentsPropInfos)
    }

    private func transStaticInitModifierList(startPos: CodePosition, modifierList: NonTerminal, lp: LocalParser) {
        var staticKeyWordPos: CodePosition = CodePosition()
        let mlp = LocalParser(modifierList.children)
        while (let Some(modifier) <- mlp.tryConsume()) {
            let mp = LocalParser((modifier as NonTerminal).getOrThrow().children)
            while (let Some(vm) <- mp.tryConsume()) {
                match (vm.kind) {
                    case SyntaxNodeKind.StaticToken =>
                        staticKeyWordPos = startPos + lp.offset + mlp.offset + mp.offset
                        break
                    case _ => continue
                }
            }
            mlp.moveOffset(modifier)
        }
        staticKeyWordPos
    }

    private func transStaticInit(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): StaticInit {
        // BNF: STATIC INIT LPAREN RPAREN LCURL expressionOrDeclarations? RCURL
        let lp = LocalParser(node.children)

        var staticKeyWordPos: CodePosition = CodePosition()

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList) && let Some(modifierList) <- (v as NonTerminal)) {
            staticKeyWordPos = transStaticInitModifierList(startPos, modifierList, lp)
            lp.offset.move(v.offset)
        }
        let initNode = lp.consume(kind: SyntaxNodeKind.InitToken)
        let initKeyWordPos = startPos + lp.offset
        lp.offset.move(initNode?.offset)
        let lParenNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
        var lParenPos = startPos + lp.offset
        lp.offset.move(lParenNode?.offset)
        let rParenNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        var rParenPos = startPos + lp.offset
        lp.offset.move(rParenNode?.offset)

        let bodyNode = lp.consume()
        let bodyPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(bodyNode?.offset)

        let propInfo = StaticInitPropInfos(bodyPropInfo)
        let posInfo = StaticInitPosInfos(staticKeyWordPos, initKeyWordPos, lParenPos, rParenPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        StaticInit(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transStructDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): StructDecl {
        // BNF: annotationList? (structModifier NL*)? STRUCT NL* identifier (NL* typeParameters)? (NL* UPPERBOUND NL* superInterfaces)? (NL* genericConstraints)? NL* structBody
        let lp = LocalParser(node.children)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }

        let structNode = lp.consume(kind: SyntaxNodeKind.StructToken)
        let structKeyWordPos = startPos + lp.offset
        lp.offset.move(structNode?.offset)

        let (name, identifierPos, genericParamsPropInfo, genericParamsLAnglePos, genericParamsCommasPos,
            genericParamsRAnglePos) = consumeNamedGenericDeclHead(lp, startPos)
        let (upperBoundPos, superTyAnnotationsPropInfo, superTyAnnotationsBitAndPos) = consumeUpperBoundSuperTypes(lp,
            startPos)
        let genericConstraintsPropInfo = consumeGenericConstraintsPropInfo(lp)
        let (bodyNode, bodyPropInfo) = consumeBodyNodeAndPropInfo(lp)
        lp.offset.move(bodyNode?.offset)

        let propInfos = StructDeclPropInfos(bodyPropInfo, genericConstraintsPropInfo, genericParamsPropInfo,
            superTyAnnotationsPropInfo.toArray())
        let posInfos = StructDeclPosInfos(structKeyWordPos, identifierPos, genericParamsLAnglePos,
            genericParamsCommasPos, genericParamsRAnglePos, upperBoundPos, superTyAnnotationsBitAndPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        StructDecl(node, startPos, parent, name, posInfos, propInfos, commentsPropInfos)
    }

    private func transInterfaceDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): InterfaceDecl {
        // BNF: annotationList? (interfaceModifierList NL*)? INTERFACE NL* identifier (NL* typeParameters NL*)? (NL* UPPERBOUND NL* superInterfaces)? (NL* genericConstraints)? (NL* interfaceBody)
        let lp = LocalParser(node.children)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }

        let interfaceNode = lp.consume(kind: SyntaxNodeKind.InterfaceToken)
        let interfaceKeyWordPos = startPos + lp.offset
        lp.offset.move(interfaceNode?.offset)

        let (name, identifierPos, genericParamsPropInfo, genericParamsLAnglePos, genericParamsCommasPos,
            genericParamsRAnglePos) = consumeNamedGenericDeclHead(lp, startPos)
        let (upperBoundPos, superTyAnnotationsPropInfo, superTyAnnotationsBitAndPos) = consumeUpperBoundSuperTypes(lp,
            startPos)
        let genericConstraintsPropInfo = consumeGenericConstraintsPropInfo(lp)
        let (bodyNode, bodyPropInfo) = consumeBodyNodeAndPropInfo(lp)
        lp.offset.move(bodyNode?.offset)

        let propInfos = InterfaceDeclPropInfos(bodyPropInfo, genericConstraintsPropInfo, genericParamsPropInfo,
            superTyAnnotationsPropInfo.toArray())
        let posInfos = InterfaceDeclPosInfos(interfaceKeyWordPos, identifierPos, genericParamsLAnglePos,
            genericParamsCommasPos, genericParamsRAnglePos, upperBoundPos, superTyAnnotationsBitAndPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        InterfaceDecl(node, startPos, parent, name, posInfos, propInfos, commentsPropInfos)
    }

    private func transExtendDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ExtendDecl {
        // BNF: annotationList? (interfaceModifierList NL*)? INTERFACE NL* identifier (NL* typeParameters NL*)? (NL* UPPERBOUND NL* superInterfaces)? (NL* genericConstraints)? (NL* interfaceBody)
        let lp = LocalParser(node.children)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }

        let extendNode = lp.consume(kind: SyntaxNodeKind.ExtendToken)
        let extendKeyWordPos = startPos + lp.offset
        lp.offset.move(extendNode?.offset)

        var genericParamsPropInfo: ?PropInfo = None
        var genericParamsCommasPos = Array<CodePosition>()
        var (genericParamsLAnglePos, genericParamsRAnglePos): (Option<CodePosition>, Option<CodePosition>) = (None, None)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.TypeArguments)) {
            genericParamsPropInfo = PropInfo(lp.cur - 1, lp.offset)
            (genericParamsLAnglePos, genericParamsCommasPos, genericParamsRAnglePos) = transGenericParams(v,
                startPos + lp.offset)
            lp.moveOffset(v)
        }

        let predictTypeAnnotation = {
            kind: SyntaxNodeKind => kind.isTypeAnnotation()
        }

        var extendedTyAnnotationPropInfo = PropInfo()
        if (lp.look(predictTypeAnnotation)) {
            let extendedTyNode = lp.consume()
            extendedTyAnnotationPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(extendedTyNode?.offset)
        }

        let (upperBoundPos, superTyAnnotationsPropInfo, superTyAnnotationsBitAndPos) = consumeUpperBoundSuperTypes(lp,
            startPos)
        let genericConstraintsPropInfo = consumeGenericConstraintsPropInfo(lp)
        let (bodyNode, bodyPropInfo) = consumeBodyNodeAndPropInfo(lp)
        lp.offset.move(bodyNode?.offset)

        let propInfos = ExtendDeclPropInfos(bodyPropInfo, extendedTyAnnotationPropInfo, genericConstraintsPropInfo,
            genericParamsPropInfo, superTyAnnotationsPropInfo.toArray())
        let posInfos = ExtendDeclPosInfos(extendKeyWordPos, genericParamsLAnglePos, genericParamsCommasPos,
            genericParamsRAnglePos, upperBoundPos, superTyAnnotationsBitAndPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ExtendDecl(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transEnumDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): EnumDecl {
        // BNF: annotationList? (enumModifier NL*)? ENUM NL* identifier (NL* typeParameters NL*)? (NL* UPPERBOUND NL* superInterfaces)? (NL* genericConstraints)? NL* LCURL end* enumBody end* RCURL
        let lp = LocalParser(node.children)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }

        let enumNode = lp.consume(kind: SyntaxNodeKind.EnumToken)
        let enumKeyWordPos = startPos + lp.offset
        lp.offset.move(enumNode?.offset)

        let (name, identifierPos, genericParamsPropInfo, genericParamsLAnglePos, genericParamsCommasPos,
            genericParamsRAnglePos) = consumeNamedGenericDeclHead(lp, startPos)
        let (upperBoundPos, superTyAnnotationsPropInfo, superTyAnnotationsBitAndPos) = consumeUpperBoundSuperTypes(lp,
            startPos)
        let genericConstraintsPropInfo = consumeGenericConstraintsPropInfo(lp)
        let (bodyNode, bodyPropInfo) = consumeBodyNodeAndPropInfo(lp)
        var body: ?Body = cast<Body>(transNode(bodyNode.getOrThrow(), startPos + lp.offset, parent))
        lp.offset.move(bodyNode?.offset)

        var caseSeparatorPos = Array<CodePosition>()
        var nonExhaustiveTripleDotPos: ?CodePosition = None
        if (let Some(bd) <- body) {
            caseSeparatorPos = bd.enumBodyPosInfos.bitOrsPos
            nonExhaustiveTripleDotPos = bd.enumBodyPosInfos.ellipsisPos
        }
        let isNonExhaustive = !nonExhaustiveTripleDotPos.isNone()

        let propInfos = EnumDeclPropInfos(bodyPropInfo, genericConstraintsPropInfo, genericParamsPropInfo,
            superTyAnnotationsPropInfo.toArray())
        let posInfos = EnumDeclPosInfos(enumKeyWordPos, identifierPos, genericParamsLAnglePos, genericParamsCommasPos,
            genericParamsRAnglePos, upperBoundPos, superTyAnnotationsBitAndPos.toArray(), caseSeparatorPos,
            nonExhaustiveTripleDotPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        EnumDecl(node, startPos, parent, isNonExhaustive, name, posInfos, propInfos, commentsPropInfos)
    }

    private func transEnumConstructor(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): EnumConstructor {
        let lp = LocalParser(node.children)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }

        var name: String = ""
        var identifierPos: CodePosition = CodePosition()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            name = getValue(v)
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }

        var lParenPos: ?CodePosition = None
        var rParenPos: ?CodePosition = None
        var paramTyAnnotationsPorpInfo = ArrayList<PropInfo>()
        var paramsCommasPos = ArrayList<CodePosition>()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            lParenPos = startPos + lp.offset
            lp.offset.move(v.offset)
            let predictTypeAnnotation = {
                kind: SyntaxNodeKind => kind.isTypeAnnotation()
            }
            while (lp.look(predictTypeAnnotation)) {
                let tyAnnoNode = lp.consume()
                paramTyAnnotationsPorpInfo.add(PropInfo(lp.cur - 1, lp.offset))
                lp.offset.move(tyAnnoNode?.offset)
                if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.CommaToken)) {
                    paramsCommasPos.add(startPos + lp.offset)
                    lp.offset.move(v.offset)
                }
            }
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RParenToken)) {
            rParenPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }

        let propInfos = EnumConstructorPropInfos(paramTyAnnotationsPorpInfo.toArray())
        let posInfos = EnumConstructorPosInfos(identifierPos, lParenPos, paramsCommasPos.toArray(), rParenPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        EnumConstructor(node, startPos, parent, name, posInfos, propInfos, commentsPropInfos)
    }

    private func transMacroDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): MacroDecl {
        // BNF: annotationList? macroModifierList? NL* MACRO NL* identifier NL* functionParameters (COLON NL* identifier NL*)? block
        let lp = LocalParser(node.children)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AnnotationList)) {
            lp.offset.move(v.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ModifierList)) {
            lp.offset.move(v.offset)
        }

        let macroNode = lp.consume(kind: SyntaxNodeKind.MacroToken)
        let macroPos = startPos + lp.offset
        lp.offset.move(macroNode?.offset)

        var name: String = ""
        var identifierPos: CodePosition = CodePosition()
        if (let Some(v) <- lp.consume(kind: SyntaxNodeKind.IdentToken)) {
            name = getValue(v)
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }

        let (paramsPropInfo, retTyAnnotationPropInfo, retTyAnnotationColonPos) = consumeParamsAndRetTyAnnotation(lp,
            startPos)

        var bodyPropInfo = PropInfo()
        if (let Some(v) <- lp.consume(kind: SyntaxNodeKind.Block)) {
            bodyPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(v.offset)
        }

        let propInfos = MacroDeclPropInfos(bodyPropInfo, paramsPropInfo, retTyAnnotationPropInfo)
        let posInfos = MacroDeclPosInfos(macroPos, identifierPos, retTyAnnotationColonPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        MacroDecl(node, startPos, parent, name, posInfos, propInfos, commentsPropInfos)
    }

    private func transMacroExpandDecl(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): MacroExpandDecl {
        let lp = LocalParser(node.children)
        let (calleePropInfo, macroAttrsPropInfo, macroInputsPropInfo, atPos, lSquarePos, rSquarePos, lParenPos,
            rParenPos) = consumeMacroExpandCommon(lp, startPos)

        let propInfos = MacroExpandDeclPropInfos(calleePropInfo, macroAttrsPropInfo, macroInputsPropInfo)
        let posInfos = MacroExpandDeclPosInfos(atPos, lSquarePos, rSquarePos, lParenPos, rParenPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        MacroExpandDecl(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transMacroExpandParam(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): MacroExpandParam {
        let lp = LocalParser(node.children)
        let (calleePropInfo, macroAttrsPropInfo, macroInputsPropInfo, atPos, lSquarePos, rSquarePos, lParenPos,
            rParenPos) = consumeMacroExpandCommon(lp, startPos)

        let propInfos = MacroExpandParamPropInfos(calleePropInfo, macroAttrsPropInfo, macroInputsPropInfo)
        let posInfos = MacroExpandParamPosInfos(atPos, lSquarePos, rSquarePos, lParenPos, rParenPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        MacroExpandParam(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    // Expr
    private func transValuedLitConstExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): LitConstExpr {
        let lp = LocalParser(node.children)
        let litNode = lp.consume().getOrThrow()
        var kind: LitConstKind = match (litNode.kind) {
            case SyntaxNodeKind.IntegerLiteralToken => LitConstKind.IntergerLiteral
            case SyntaxNodeKind.FloatLiteralToken => LitConstKind.FloatLiteral
            case SyntaxNodeKind.BooleanLiteralToken => LitConstKind.BoolLiteral
            case _ => throw Exception(
                "ParseException: This SyntaxNode kind is not integer literal, float literal or bool literal.")
        }
        let rawValue = match (litNode) {
            case vt: ValuedTerminal => vt.value
            case _ => ""
        }
        lp.moveOffset(litNode)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        LitConstExpr(node, startPos, parent, kind, rawValue, commentsPropInfos)
    }

    private func transUnitLitConstExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): LitConstExpr {
        let lp = LocalParser(node.children)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        LitConstExpr(node, startPos, parent, LitConstKind.UnitLiteral, node.toString(), commentsPropInfos)
    }

    private func transLitConstRuneExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): LitConstRuneExpr {
        var rawValue = ""
        var isSingleQuote = false
        for (child in node.children) {
            match (child) {
                case vt: ValuedTerminal => rawValue = vt.toString()
                case t: Terminal => isSingleQuote = getIsSingleQuote(t)
                case _ => ()
            }
        }
        let lp = LocalParser(node.children)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        LitConstRuneExpr(node, startPos, parent, rawValue, isSingleQuote, commentsPropInfos)
    }

    private func transInterpolationExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): StrInterpolationContent {
        // BNF: $ + Block
        let lp = LocalParser(node.children)
        let dollarPos = startPos
        lp.offset.move(lp.consume()?.offset)
        lp.consume()
        let blockPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        StrInterpolationContent(node, startPos, parent, StrInterpolationContentPosInfos(dollarPos),
            StrInterpolationContentPropInfos(blockPropInfo), commentsPropInfos)
    }

    private func transStringLitConstExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): LitConstStrExpr {
        let lp = LocalParser(node.children)
        var kind = match (node.kind) {
            case SyntaxNodeKind.LineStringLiteral => LitConstStrKind.StringLiteral
            // case SyntaxNodeKind.JStringLiteral
            case SyntaxNodeKind.MultiLineStringLiteral => LitConstStrKind.MultiLineString
            case SyntaxNodeKind.MultiLineRawStringLiteral => LitConstStrKind.MultiLineRawString
            case _ => throw Exception("ParseException: This SyntaxNode kind is not string literal.")
        }
        var delimiterNum = 0
        var isSingleQuote = false
        var rawValue = ""
        var strPartExprs = ArrayList<StrLiteralPart>()
        var findStart = false
        var strPartExprsPropInfo = PropInfo()

        for (i in 0..node.children.size) {
            let child = node.children[i]
            match (child.kind) {
                case SyntaxNodeKind.StringLiteralToken => rawValue += child.toString()
                case SyntaxNodeKind.HashToken =>
                    if (let Some(rt) <- (child as RepeatedTerminal)) {
                        delimiterNum = rt.repeat
                    }
                case SyntaxNodeKind.SingleQuoteToken => isSingleQuote = true
                case SyntaxNodeKind.InterpolationExpr =>
                    if (!findStart) {
                        findStart = true
                        strPartExprsPropInfo = PropInfo(i, lp.offset)
                    }
                    if (let Some(interpol) <- cast<StrInterpolationContent>(transNode(child, startPos + lp.offset, None))) {
                        rawValue += interpol.toString()
                    }
                case SyntaxNodeKind.LineStringLiteral | SyntaxNodeKind.MultiLineStringLiteral
                    | SyntaxNodeKind.MultiLineRawStringLiteral =>
                    if (!findStart) {
                        findStart = true
                        strPartExprsPropInfo = PropInfo(i, lp.offset)
                    }
                    if (let Some(litConst) <- cast<LitConstExpr>(transNode(child, startPos + lp.offset, None))) {
                        rawValue += litConst.rawValue
                    }
                case _ => ()
            }
            lp.moveOffset(child)
        }
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        LitConstStrExpr(node, startPos, parent, delimiterNum, isSingleQuote, rawValue, kind,
            LitConstStrExprPropInfos(strPartExprsPropInfo), commentsPropInfos)
    }

    private func getIsSingleQuote(node: Terminal): Bool {
        return match (node.kind) {
            case SyntaxNodeKind.SingleQuoteToken | SyntaxNodeKind.TripleSingleQuoteToken => true
            case SyntaxNodeKind.DoubleQuoteToken | SyntaxNodeKind.TripleDoubleQuoteToken => false
            case _ => false
        }
    }

    private func transSymbolRef(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): SymbolRef {
        var identifierPos = CodePosition()
        var lAnglePos: ?CodePosition = None
        var rAnglePos: ?CodePosition = None
        var commasPos = ArrayList<CodePosition>()
        var name = ""
        var typeArgumentsPropInfo: ?PropInfo = None

        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            identifierPos = startPos + lp.offset
            lp.offset.move(v.offset)
            name = getValue(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.TypeArguments)) {
            typeArgumentsPropInfo = PropInfo(lp.cur - 1, lp.offset)
            (lAnglePos, commasPos, rAnglePos) = transTypeArguments(v, startPos + lp.offset)
        }

        let propInfos = SymbolRefPropInfos(typeArgumentsPropInfo)
        let posInfo = SymbolRefPosInfos(identifierPos, lAnglePos, rAnglePos, commasPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        SymbolRef(node, startPos, parent, name, posInfo, propInfos, commentsPropInfos)
    }

    private func transArrayLiteralExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ArrayLiteral {
        // BNF: LSQUARE (NL* elements)? NL* RSQUARE
        let lSquarePos = startPos
        var rSquarePos = CodePosition()
        let lp = LocalParser(node.children)
        lp.moveOffset(lp.consume(kind: SyntaxNodeKind.LSquareToken))
        let predictExpr = {
            kind: SyntaxNodeKind => kind.isExpr()
        }
        let sep = SyntaxNodeKind.CommaToken
        let (exprsPropInfo, commasPos) = consumeSeparatedElementsStartPropInfo(lp, startPos, predictExpr, sep)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RSquareToken)) {
            rSquarePos = startPos + lp.offset
        }
        let posInfo = ArrayLiteralPosInfos(lSquarePos, rSquarePos, commasPos.toArray())
        let propInfo = ArrayLiteralPropInfos(exprsPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ArrayLiteral(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transTupleLiteralExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): TupleLiteral {
        // BNF: LSQUARE (NL* elements)? NL* RSQUARE
        let lParenPos = startPos
        var rParenPos = CodePosition()
        let lp = LocalParser(node.children)
        lp.moveOffset(lp.consume(kind: SyntaxNodeKind.LParenToken))
        let predictTupleElement = {
            kind: SyntaxNodeKind => kind.isExpr() || kind == SyntaxNodeKind.WildcardPattern
        }
        let sep = SyntaxNodeKind.CommaToken
        let (exprsPropInfo, commasPos) = consumeSeparatedElementsStartPropInfo(lp, startPos, predictTupleElement, sep)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RParenToken)) {
            rParenPos = startPos + lp.offset
        }
        let posInfo = TupleLiteralPosInfos(lParenPos, rParenPos, commasPos.toArray())
        let propInfo = TupleLiteralPropInfos(exprsPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        TupleLiteral(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transVArrayExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): VArrayExpr {
        // BNF: VArray<Int64, $5>({ i => i})
        var argumentPropInfo = PropInfo()
        var vArrayTypePropInfo = PropInfo()
        var lParenPos = CodePosition()
        var rParenPos = CodePosition()
        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.VArrayType)) {
            vArrayTypePropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            lParenPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.Argument)) {
            argumentPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RParenToken)) {
            rParenPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        let posInfo = VArrayExprPosInfos(lParenPos, rParenPos)
        let propInfo = VArrayExprPropInfos(argumentPropInfo, vArrayTypePropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        VArrayExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transCallExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): CallExpr {
        // BNF: expr LPAREN NL* (valueArgument (NL* COMMA NL* valueArgument)* NL*)? RPAREN
        var argumentPropInfo = ArrayList<PropInfo>()
        var calleePropInfo = PropInfo()
        var lParenPos = CodePosition()
        var rParenPos = CodePosition()
        let lp = LocalParser(node.children)
        let commasPos = ArrayList<CodePosition>()

        let predictExpr = {
            kind: SyntaxNodeKind => kind.isExpr()
        }
        if (lp.look(predictExpr)) {
            let exprNode = lp.consume()
            calleePropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(exprNode?.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            lParenPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        let sep = SyntaxNodeKind.CommaToken
        while (lp.look(SyntaxNodeKind.Argument)) {
            let argumentNode = lp.consume()
            argumentPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.offset.move(argumentNode?.offset)
            if (let Some(v) <- lp.lookAndConsume(sep)) {
                commasPos.add(startPos + lp.offset)
                lp.offset.move(v.offset)
            }
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RParenToken)) {
            rParenPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }

        let propInfos = CallExprPropInfos(calleePropInfo, argumentPropInfo.toArray())
        let posInfos = CallExprPosInfos(lParenPos, rParenPos, commasPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        CallExpr(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transTypeConvExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): TypeConvExpr {
        //BNF:  (INT8...) LPAREN NL* expression NL* RPAREN
        let lp = LocalParser(node.children)

        let annoNode = lp.consume()
        let targetTypeAnnotationPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(annoNode?.offset)

        let lParenNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
        let lParenPos = startPos + lp.offset
        lp.offset.move(lParenNode?.offset)

        let srcValNode = lp.consume()
        let srcValPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(srcValNode?.offset)

        let rParenNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        let rParenPos = startPos + lp.offset
        lp.offset.move(rParenNode?.offset)

        let propInfo = TypeConvExprPropInfos(srcValPropInfo, targetTypeAnnotationPropInfo)
        let posInfo = TypeConvExprPosInfos(lParenPos, rParenPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        TypeConvExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transOptionalExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): OptionalExpr {
        let lp = LocalParser(node.children)
        var curNode = lp.consume()
        let basePropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)

        curNode = lp.consume(kind: SyntaxNodeKind.QuestToken)
        let questPos = startPos + lp.offset
        let posInfo = OptionalExprPosInfos(questPos)
        let propInfo = OptionalExprPropInfos(basePropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        OptionalExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transAssignOpKind(node: SyntaxNodeImpl): AssignOpKind {
        return match (node) {
            case t: Terminal => match (t.kind) {
                case SyntaxNodeKind.AssignToken => AssignOpKind.Assign
                case SyntaxNodeKind.AddAssignToken => AssignOpKind.AddAssign
                case SyntaxNodeKind.SubAssignToken => AssignOpKind.SubAssign
                case SyntaxNodeKind.MulAssignToken => AssignOpKind.MulAssign
                case SyntaxNodeKind.ExpAssignToken => AssignOpKind.ExpAssign
                case SyntaxNodeKind.DivAssignToken => AssignOpKind.DivAssign
                case SyntaxNodeKind.ModAssignToken => AssignOpKind.ModAssign
                case SyntaxNodeKind.AndAssignToken => AssignOpKind.AndAssign
                case SyntaxNodeKind.OrAssignToken => AssignOpKind.OrAssign
                case SyntaxNodeKind.BitAndAssignToken => AssignOpKind.BitAndAssign
                case SyntaxNodeKind.BitOrAssignToken => AssignOpKind.BitOrAssign
                case SyntaxNodeKind.BitXorAssignToken => AssignOpKind.BitXorAssign
                case SyntaxNodeKind.LShiftAssignToken => AssignOpKind.LShiftAssign
                case SyntaxNodeKind.RShiftAssignToken => AssignOpKind.RShiftAssign
                case _ => throw Exception("ParseException: This SyntaxNode kind is not assign operator.")
            }
            case _ => throw Exception("ParseException: This SyntaxNode kind is not assign operator.")
        }
    }

    private func transAssignExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): AssignExpr {
        let lp = LocalParser(node.children)
        // lhs
        var curNode = lp.consume()
        let lhsPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)
        var opKind: AssignOpKind = AssignOpKind.Assign
        var opPos = CodePosition()
        if (let Some(v) <- lp.consume()) {
            opKind = transAssignOpKind(v)
            opPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        //rhs
        curNode = lp.consume()
        let rhsPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)

        let posInfo = AssignExprPosInfos(opPos)
        let propInfo = AssignExprPropInfos(lhsPropInfo, rhsPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        AssignExpr(node, startPos, parent, opKind, posInfo, propInfo, commentsPropInfos)
    }

    private func transBinaryOpKind(node: SyntaxNodeImpl): BinaryOpKind {
        return match (node) {
            case t: Terminal => match (t.kind) {
                case SyntaxNodeKind.AddToken => BinaryOpKind.Add
                case SyntaxNodeKind.AndToken => BinaryOpKind.And
                case SyntaxNodeKind.BitAndToken => BinaryOpKind.BitAnd
                case SyntaxNodeKind.BitOrToken => BinaryOpKind.BitOr
                case SyntaxNodeKind.BitXorToken => BinaryOpKind.BitXor
                case SyntaxNodeKind.CoalescingToken => BinaryOpKind.Coalescing
                case SyntaxNodeKind.CompositionToken => BinaryOpKind.Composition
                case SyntaxNodeKind.DivToken => BinaryOpKind.Div
                case SyntaxNodeKind.EqualToken => BinaryOpKind.Equal
                case SyntaxNodeKind.ExpToken => BinaryOpKind.Exp
                case SyntaxNodeKind.GeToken => BinaryOpKind.Ge
                case SyntaxNodeKind.GtToken => BinaryOpKind.Gt
                case SyntaxNodeKind.LeToken => BinaryOpKind.Le
                case SyntaxNodeKind.LShiftToken => BinaryOpKind.LShift
                case SyntaxNodeKind.LtToken => BinaryOpKind.Lt
                case SyntaxNodeKind.ModToken => BinaryOpKind.Mod
                case SyntaxNodeKind.MulToken => BinaryOpKind.Mul
                case SyntaxNodeKind.NotEqToken => BinaryOpKind.NotEq
                case SyntaxNodeKind.OrToken => BinaryOpKind.Or
                case SyntaxNodeKind.PipelineToken => BinaryOpKind.Pipeline
                case SyntaxNodeKind.RShiftToken => BinaryOpKind.RShift
                case SyntaxNodeKind.SubToken => BinaryOpKind.Sub
                case _ => throw Exception("ParseException: This SyntaxNode kind is not binary operator.")
            }
            case _ => throw Exception("ParseException: This SyntaxNode kind is not binary operator.")
        }
    }

    private func transBinaryExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): BinaryExpr {
        // BNF: lhs op rhs
        let lp = LocalParser(node.children)
        let lhsNode = lp.consume()
        let lhsPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(lhsNode?.offset)

        let op = lp.consume().getOrThrow()
        let opKind = transBinaryOpKind(op)
        let opPos = startPos + lp.offset
        lp.offset.move(op.offset)

        let rhsNode = lp.consume()
        let rhsPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(rhsNode?.offset)

        let propInfos = BinaryExprPropInfos(lhsPropInfo, rhsPropInfo)
        let posInfo = BinaryExprPosInfos(opPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        BinaryExpr(node, startPos, parent, opKind, posInfo, propInfos, commentsPropInfos)
    }

    private func transUnaryExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): UnaryExpr {
        let lp = LocalParser(node.children)
        let op = lp.consume().getOrThrow()
        var opKind: UnaryOpKind = match (op) {
            case t: Terminal => match (t.kind) {
                case SyntaxNodeKind.NotToken => UnaryOpKind.Not
                case SyntaxNodeKind.SubToken => UnaryOpKind.Sub
                case _ => throw Exception("ParseException: This SyntaxNode kind is not unary operator.")
            }
            case _ => throw Exception("ParseException: This SyntaxNode kind is not unary operator.")
        }
        let opPos = startPos + lp.offset
        let curNode = lp.consume()
        let operandPropInfo = PropInfo(lp.cur - 1, lp.offset)
        let posInfo = UnaryExprPosInfos(opPos)
        let propInfo = UnaryExprPropInfos(operandPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        UnaryExpr(node, startPos, parent, opKind, posInfo, propInfo, commentsPropInfos)
    }

    private func transIsExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): IsExpr {
        let lp = LocalParser(node.children)

        let exprNode = lp.consume()
        let exprPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(exprNode?.offset)

        let isNode = lp.consume(kind: SyntaxNodeKind.IsToken)
        let isPos = startPos + lp.offset
        lp.offset.move(isNode?.offset)

        let typeNode = lp.consume()
        let typePropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(typeNode?.offset)

        let propInfo = IsExprPropInfos(exprPropInfo, typePropInfo)
        let posInfo = IsExprPosInfos(isPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        IsExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transAsExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): AsExpr {
        // BNF: expr AS targetType
        let lp = LocalParser(node.children)

        let exprNode = lp.consume()
        let exprPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(exprNode?.offset)

        let asNode = lp.consume(kind: SyntaxNodeKind.AsToken)
        let asPos = startPos + lp.offset
        lp.offset.move(asNode?.offset)

        let typeNode = lp.consume()
        let typePropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(typeNode?.offset)

        let propInfo = AsExprPropInfos(exprPropInfo, typePropInfo)
        let posInfo = AsExprPosInfos(asPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        AsExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transParenExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ParenExpr {
        // BNF: LPAREN expr RPAREN
        let lp = LocalParser(node.children)

        let lParenNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
        let lParenPos = startPos + lp.offset
        lp.offset.move(lParenNode?.offset)
        let exprNode = lp.consume()
        let exprPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(exprNode?.offset)
        let rParenNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        let rParenPos = startPos + lp.offset
        lp.offset.move(rParenNode?.offset)

        let propInfo = ParenExprPropInfos(exprPropInfo)
        let posInfo = ParenExprPosInfos(lParenPos, rParenPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ParenExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transReturnExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ReturnExpr {
        // BNF: RETURN (NL* expression)?
        let lp = LocalParser(node.children)
        var curNode = lp.consume(kind: SyntaxNodeKind.ReturnToken)
        let returnPos = startPos + lp.offset
        lp.moveOffset(curNode)

        var retValPropInfo: ?PropInfo = None
        if (!lp.isEnd()) {
            if (lp.tryConsume().isSome()) {
                retValPropInfo = PropInfo(lp.cur - 1, lp.offset)
            }
        }
        let posInfo = ReturnExprPosInfos(returnPos)
        let propInfo = (ReturnExprPropInfos(retValPropInfo))
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ReturnExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transIncOrDecExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): IncOrDecExpr {
        // BNF: postfixExpression (INC | DEC )
        let lp = LocalParser(node.children)

        var kind: IncOrDecOpKind = IncOrDecOpKind.Decr

        let exprNode = lp.consume()
        let exprPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(exprNode?.offset)

        var opPos = CodePosition()
        if (lp.look(SyntaxNodeKind.IncrToken)) {
            kind = IncOrDecOpKind.Incr
            let opNode = lp.consume(kind: SyntaxNodeKind.IncrToken)
            opPos = startPos + lp.offset
            lp.offset.move(opNode?.offset)
        } else {
            kind = IncOrDecOpKind.Decr
            let opNode = lp.consume(kind: SyntaxNodeKind.DecrToken)
            opPos = startPos + lp.offset
            lp.offset.move(opNode?.offset)
        }

        let propInfo = IncOrDecExprPropInfos(exprPropInfo)
        let posInfo = IncOrDecExprPosInfos(opPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        IncOrDecExpr(node, startPos, parent, kind, posInfo, propInfo, commentsPropInfos)
    }

    private func transRangeExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): RangeExpr {
        // BNF: shiftingExpression NL* rangeOp NL* shiftingExpression (NL* COLON NL* shiftingExpression)?
        let lp = LocalParser(node.children)

        var startPropInfo: ?PropInfo = None
        if (!lp.look(SyntaxNodeKind.RangeOpToken) && !lp.look(SyntaxNodeKind.ClosedRangeOpToken)) {
            let startNode = lp.consume()
            startPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(startNode?.offset)
        }
        let op = lp.consume().getOrThrow()
        var kind: RangeKind = match (op) {
            case t: Terminal => match (t.kind) {
                case SyntaxNodeKind.RangeOpToken => RangeKind.RangeOp
                case SyntaxNodeKind.ClosedRangeOpToken => RangeKind.ClosedRangeOp
                case _ => throw Exception("ParseException: This SyntaxNode kind is not range operator.")
            }
            case _ => throw Exception("ParseException: This SyntaxNode kind is not range operator.")
        }
        var opPos = startPos + lp.offset
        lp.offset.move(op.offset)

        var endPropInfo: ?PropInfo = None
        if (!lp.look(SyntaxNodeKind.ColonToken) && !lp.isEnd()) {
            if (let Some(endNode) <- lp.tryConsume()) {
                endPropInfo = PropInfo(lp.cur - 1, lp.offset)
                lp.offset.move(endNode.offset)
            }
        }
        var colonPos: ?CodePosition = None
        var stepPropInfo: ?PropInfo = None

        if (lp.look(SyntaxNodeKind.ColonToken)) {
            let colonNode = lp.consume(kind: SyntaxNodeKind.ColonToken)
            colonPos = startPos + lp.offset
            lp.offset.move(colonNode?.offset)

            let stepNode = lp.consume()
            stepPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(stepNode?.offset)
        }

        let propInfo = RangeExprPropInfos(startPropInfo, stepPropInfo, endPropInfo)
        let posInfo = RangeExprPosInfos(opPos, colonPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        RangeExpr(node, startPos, parent, kind, posInfo, propInfo, commentsPropInfos)
    }

    private func transBreakExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): BreakExpr {
        let lp = LocalParser(node.children)
        if (lp.look(SyntaxNodeKind.BreakToken)) {
            let breakNode = lp.consume()
            lp.offset.move(breakNode?.offset)
        }
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        BreakExpr(node, startPos, parent, commentsPropInfos)
    }

    private func transContinueExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ContinueExpr {
        let lp = LocalParser(node.children)
        if (lp.look(SyntaxNodeKind.ContinueToken)) {
            let continueNode = lp.consume()
            lp.offset.move(continueNode?.offset)
        }
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ContinueExpr(node, startPos, parent, commentsPropInfos)
    }

    private func transMemberAccess(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): MemberAccess {
        // BNF: base.field
        var basePropInfo = PropInfo()
        var field: ?SymbolRef = None
        var dotPos = CodePosition()
        let lp = LocalParser(node.children)

        let predictNode = {
            kind: SyntaxNodeKind => kind.isExpr() || kind.isTypeAnnotation()
        }
        if (lp.look(predictNode)) {
            let baseNode = lp.consume()
            basePropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.offset.move(baseNode?.offset)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.DotToken)) {
            dotPos = startPos + lp.offset
            lp.offset.move(v.offset)
        }
        let fieldNode = lp.consume(kind: SyntaxNodeKind.RefExpr)
        let fieldPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.offset.move(fieldNode?.offset)

        let propInfos = MemberAccessPropInfos(basePropInfo, fieldPropInfo)
        let posInfo = MemberAccessPosInfos(dotPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        MemberAccess(node, startPos, parent, posInfo, propInfos, commentsPropInfos)
    }

    private func transSubscriptExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): SubscriptExpr {
        // BNF: expression LSQUARE NL* (expression | rangeElement) NL* RSQUARE
        var basePropInfo = PropInfo()
        var indexsPropInfo = PropInfo()
        var lSquarePos = CodePosition()
        var rSquarePos = CodePosition()
        var commasPos = ArrayList<CodePosition>()
        let lp = LocalParser(node.children)
        let predictExpr = {
            kind: SyntaxNodeKind => kind.isExpr()
        }
        if (lp.look(predictExpr)) {
            if (let Some(node) <- lp.consume()) {
                basePropInfo = PropInfo(lp.cur - 1, lp.offset)
                lp.moveOffset(node)
            }
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LSquareToken)) {
            lSquarePos = startPos + lp.offset
            lp.moveOffset(v)
        }
        var findFirstIndex = false
        let sep = SyntaxNodeKind.CommaToken
        while (lp.look(predictExpr)) {
            if (let Some(node) <- lp.consume()) {
                if (!findFirstIndex) {
                    findFirstIndex = true
                    indexsPropInfo = PropInfo(lp.cur - 1, lp.offset)
                }
                lp.moveOffset(node)
            }
            if (lp.look(sep)) {
                var curNode = lp.consume(kind: sep)
                commasPos.add(startPos + lp.offset)
                lp.moveOffset(curNode)
            }
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RSquareToken)) {
            rSquarePos = startPos + lp.offset
        }
        let posInfo = SubscriptExprPosInfos(lSquarePos, rSquarePos, commasPos.toArray())
        let propInfo = SubscriptExprPropInfos(basePropInfo, indexsPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        SubscriptExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transLambda(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): Lambda {
        // BNF: LCURL NL* lambdaParameters? NL* DOUBLE_ARROW NL* expressionOrDeclarations? RCURL
        var bodyPropInfo: ?PropInfo = None
        var lCurlPos = CodePosition()
        var rCurlPos = CodePosition()
        var doubleArrowPos: ?CodePosition = None
        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LCurlToken)) {
            lCurlPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        var curNode = lp.consume(kind: SyntaxNodeKind.ParameterList)
        let paramsPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.DoubleArrowToken)) {
            doubleArrowPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        let predictExprOrDecl = {
            kind: SyntaxNodeKind => kind.isExpr() || kind.isDecl()
        }
        var findFirst = false
        while (lp.look(predictExprOrDecl)) {
            if (let Some(node) <- lp.consume()) {
                if (!findFirst) {
                    findFirst = true
                    bodyPropInfo = PropInfo(lp.cur - 1, lp.offset)
                }
                lp.moveOffset(node)
            }
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RCurlToken)) {
            rCurlPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        let posInfo = LambdaPosInfos(lCurlPos, rCurlPos, doubleArrowPos)
        let propInfo = LambdaPropInfos(bodyPropInfo, paramsPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        Lambda(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transThrowExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ThrowExpr {
        // BNF: THROW NL* expression
        var throwValPropInfo = PropInfo()
        var throwKeyWordPos = CodePosition()
        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ThrowToken)) {
            throwKeyWordPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        let predictExpr = {
            kind: SyntaxNodeKind => kind.isExpr()
        }
        if (lp.look(predictExpr)) {
            if (let Some(node) <- lp.consume()) {
                throwValPropInfo = PropInfo(lp.cur - 1, lp.offset)
            }
        }
        let posInfo = ThrowExprPosInfos(throwKeyWordPos)
        let propInfo = ThrowExprPropInfos(throwValPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ThrowExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transSynchronizedExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): SynchronizedExpr {
        var structuredMutexPropInfo = PropInfo()
        var blockPropInfo = PropInfo()
        let lp = LocalParser(node.children)
        var curNode = lp.consume(kind: SyntaxNodeKind.SynchronizedToken)
        let synchronizedKeywordPos = startPos + lp.offset
        lp.moveOffset(curNode)
        curNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
        let lParenPos = startPos + lp.offset
        lp.moveOffset(curNode)
        curNode = lp.consume()
        structuredMutexPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)

        curNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        let rParenPos = startPos + lp.offset
        lp.moveOffset(curNode)
        if (lp.look(SyntaxNodeKind.Block)) {
            curNode = lp.consume()
            blockPropInfo = PropInfo(lp.cur - 1, lp.offset)
        }
        let posInfo = SynchronizedExprPosInfos(lParenPos, rParenPos, synchronizedKeywordPos)
        let propInfo = SynchronizedExprPropInfos(blockPropInfo, structuredMutexPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        SynchronizedExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transSpawnExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): SpawnExpr {
        var threadContextPropInfo: ?PropInfo = None
        var trailingLambdaExprPropInfo = PropInfo()
        var lParenPos: ?CodePosition = None
        var rParenPos: ?CodePosition = None
        let lp = LocalParser(node.children)
        var curNode = lp.consume(kind: SyntaxNodeKind.SpawnToken)
        let spawnKeyWordPos = startPos + lp.offset
        lp.moveOffset(curNode)
        if (lp.look(SyntaxNodeKind.LParenToken)) {
            curNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
            lParenPos = startPos + lp.offset
            lp.moveOffset(curNode)
            curNode = lp.consume()
            threadContextPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(curNode)
            curNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
            rParenPos = startPos + lp.offset
            lp.moveOffset(curNode)
        }

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.Lambda)) {
            trailingLambdaExprPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)
        }
        let posInfo = SpawnExprPosInfos(spawnKeyWordPos, lParenPos, rParenPos)
        let propInfo = SpawnExprPropInfos(threadContextPropInfo, trailingLambdaExprPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        SpawnExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transIfExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): IfExpr {
        var conditionPropInfo = PropInfo()
        var ifBlockPropInfo = PropInfo()
        var elseBlockPropInfo: ?PropInfo = None
        var elseIfPropInfo: ?PropInfo = None
        var elseKeyWordPos: ?CodePosition = None
        let lp = LocalParser(node.children)
        var curNode = lp.consume(kind: SyntaxNodeKind.IfToken)
        let ifKeyWordPos = startPos + lp.offset
        lp.moveOffset(curNode)
        curNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
        let condLParenPos = startPos + lp.offset
        lp.moveOffset(curNode)
        curNode = lp.consume()
        conditionPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)
        curNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        let condRParenPos = startPos + lp.offset
        lp.moveOffset(curNode)

        curNode = lp.consume()
        ifBlockPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ElseToken)) {
            elseKeyWordPos = startPos + lp.offset
            lp.moveOffset(curNode)
            if (lp.look(SyntaxNodeKind.Block)) {
                curNode = lp.consume()
                elseBlockPropInfo = PropInfo(lp.cur - 1, lp.offset)
                lp.moveOffset(curNode)
            }
            if (lp.look(SyntaxNodeKind.IfExpr)) {
                curNode = lp.consume()
                elseIfPropInfo = PropInfo(lp.cur - 1, lp.offset)
                lp.moveOffset(curNode)
            }
        }
        let posInfo = IfExprPosInfos(ifKeyWordPos, condLParenPos, condRParenPos, elseKeyWordPos)
        let propInfo = IfExprPropInfos(conditionPropInfo, elseBlockPropInfo, elseIfPropInfo, ifBlockPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        IfExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transParenCondition(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ParenCondition {
        // BNF: LPAREN NL* condition NL* RPAREN
        let lp = LocalParser(node.children)

        let lParenNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
        let lParenPos = startPos + lp.offset
        lp.moveOffset(lParenNode)

        let condNode = lp.consume()
        let condPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(condNode)

        let rParenNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        let rParenPos = startPos + lp.offset
        lp.moveOffset(rParenNode)

        let propInfos = ParenConditionPropInfo(condPropInfo)
        let posInfos = ParenConditionPosInfo(lParenPos, rParenPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ParenCondition(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transDisjunctionCondition(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): DisjunctionCondition {
        // BNF: conjunctionCondition NL* (OR NL* conjunctionCondition NL*)*
        var condPropInfo = ArrayList<PropInfo>()
        var orPos = ArrayList<CodePosition>()
        let lp = LocalParser(node.children)
        while (lp.look(SyntaxNodeKind.ConjunctionCondition)) {
            let conjuncNode = lp.consume(kind: SyntaxNodeKind.ConjunctionCondition)
            condPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.moveOffset(conjuncNode)

            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.OrToken)) {
                orPos.add(startPos + lp.offset)
                lp.moveOffset(v)
            }
        }

        let propInfos = DisjunctionConditionPropInfos(condPropInfo.toArray())
        let posInfos = DisjunctionConditionPosInfos(orPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        DisjunctionCondition(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transConjunctionCondition(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ConjunctionCondition {
        // BNF: atomicCondition NL* (AND NL* atomicCondition NL*)*
        let condPropInfo = ArrayList<PropInfo>()
        var andPos = ArrayList<CodePosition>()
        let lp = LocalParser(node.children)

        let predictExpr = {
            kind: SyntaxNodeKind => kind.isExpr()
        }
        while (lp.look(SyntaxNodeKind.LetPattern) || lp.look(SyntaxNodeKind.ParenCondition) || lp.look(predictExpr)) {
            let node = lp.consume()
            condPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.moveOffset(node)

            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.AndToken)) {
                andPos.add(startPos + lp.offset)
                lp.moveOffset(v)
            }
        }

        let propInfos = ConjunctionConditionPropInfos(condPropInfo.toArray())
        let posInfos = ConjunctionConditionPosInfos(andPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ConjunctionCondition(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transLetPattern(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): LetPattern {
        // LET NL* pattern (NL* BITOR NL* pattern)* NL* BACKARROW NL* rangeExpression
        var exprPropInfo = PropInfo()
        let patternsPropInfo = ArrayList<PropInfo>()
        var bitOrPos = ArrayList<CodePosition>()
        let lp = LocalParser(node.children)

        let letNode = lp.consume(kind: SyntaxNodeKind.LetToken)
        let letPos = startPos + lp.offset
        lp.moveOffset(letNode)

        let predictPattern = {
            kind: SyntaxNodeKind => kind.isPattern()
        }
        while (lp.look(predictPattern)) {
            let patternNode = lp.consume()
            patternsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.moveOffset(patternNode)

            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.BitOrToken)) {
                bitOrPos.add(startPos + lp.offset)
                lp.moveOffset(v)
            }
        }
        let backArrowNode = lp.consume(kind: SyntaxNodeKind.BackArrowToken)
        let backArrowPos = startPos + lp.offset
        lp.moveOffset(backArrowNode)

        let predictExpr = {
            kind: SyntaxNodeKind => kind.isExpr()
        }
        if (lp.look(predictExpr)) {
            let exprNode = lp.consume()
            exprPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(exprNode)
        }

        let propInfos = LetPatternPropInfo(exprPropInfo, patternsPropInfo.toArray())
        let posInfos = LetPatternPosInfo(letPos, bitOrPos.toArray(), backArrowPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        LetPattern(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transWhileExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): WhileExpr {
        var bodyPropInfo = PropInfo()
        let lp = LocalParser(node.children)
        var curNode = lp.consume(kind: SyntaxNodeKind.WhileToken)
        let whileKeyWordPos = startPos + lp.offset
        lp.moveOffset(curNode)
        let (conditionPropInfo, lParenPos, rParenPos) = consumeParenConditionPropInfo(lp, startPos)
        if (lp.look(SyntaxNodeKind.Block)) {
            curNode = lp.consume(kind: SyntaxNodeKind.Block)
            bodyPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(curNode)
        }
        let posInfo = WhileExprPosInfos(lParenPos, rParenPos, whileKeyWordPos)
        let propInfo = WhileExprPropInfos(bodyPropInfo, conditionPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        WhileExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transDoWhileExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): DoWhileExpr {
        var bodyPropInfo = PropInfo()
        let lp = LocalParser(node.children)
        var curNode = lp.consume(kind: SyntaxNodeKind.DoToken)
        let doKeyWordPos = startPos + lp.offset
        lp.moveOffset(curNode)

        if (lp.look(SyntaxNodeKind.Block)) {
            curNode = lp.consume(kind: SyntaxNodeKind.Block)
            bodyPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(curNode)
        }
        curNode = lp.consume(kind: SyntaxNodeKind.WhileToken)
        let whileKeyWordPos = startPos + lp.offset
        lp.moveOffset(curNode)
        let (conditionPropInfo, lParenPos, rParenPos) = consumeParenConditionPropInfo(lp, startPos)
        let posInfo = DoWhileExprPosInfos(lParenPos, rParenPos, doKeyWordPos, whileKeyWordPos)
        let propInfo = DoWhileExprPropInfos(bodyPropInfo, conditionPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        DoWhileExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transForInExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ForInExpr {
        var exprPropInfo = PropInfo()
        var patternPropInfo = PropInfo()
        var patternGuardPropInfo: ?PropInfo = None
        var bodyPropInfo = PropInfo()
        var whereKeyWordPos: ?CodePosition = None
        let lp = LocalParser(node.children)

        var curNode = lp.consume(kind: SyntaxNodeKind.ForToken)
        let forKeyWordPos = startPos + lp.offset
        lp.moveOffset(curNode)
        curNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
        let lParenPos = startPos + lp.offset
        lp.moveOffset(curNode)
        curNode = lp.consume()
        patternPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)

        curNode = lp.consume(kind: SyntaxNodeKind.InToken)
        let inKeywordPos = startPos + lp.offset
        lp.moveOffset(curNode)

        curNode = lp.consume()
        exprPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)

        if (lp.look(SyntaxNodeKind.WhereToken)) {
            curNode = lp.consume(kind: SyntaxNodeKind.WhereToken)
            whereKeyWordPos = startPos + lp.offset
            lp.moveOffset(curNode)
            curNode = lp.consume()
            patternGuardPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(curNode)
        }
        curNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        let rParenPos = startPos + lp.offset
        lp.moveOffset(curNode)
        if (lp.look(SyntaxNodeKind.Block)) {
            curNode = lp.consume()
            bodyPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(curNode)
        }
        let posInfo = ForInExprPosInfos(forKeyWordPos, inKeywordPos, lParenPos, rParenPos, whereKeyWordPos)
        let propInfo = ForInExprPropInfos(bodyPropInfo, exprPropInfo, patternPropInfo, patternGuardPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ForInExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transTryCatch(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): TryCatch {
        // BNF: TRY NL* block NL* FINALLY NL* block
        //      | TRY NL* block (NL* CATCH NL* LPAREN NL* catchPattern NL* RPAREN NL* block)+ (NL* FINALLY NL* block)?
        //      | TRY NL* LPAREN NL* resourceSpecifications NL* RPAREN NL* block
        //          (NL* CATCH NL* LPAREN NL* catchPattern NL* RPAREN NL* block)* (NL* FINALLY NL* block)?
        var (catchBlocksPropInfo, catchPatternsPropInfo) = (ArrayList<PropInfo>(), ArrayList<PropInfo>())
        var (resourceSpecPropInfo, finallyBlockPropInfo): (?PropInfo, ?PropInfo) = (None, None)
        var (resourceSpecCommasPos, catchKeyWordsPos) = (ArrayList<CodePosition>(), ArrayList<CodePosition>())
        var (catchLParensPos, catchRParensPos) = (ArrayList<CodePosition>(), ArrayList<CodePosition>())

        let lp = LocalParser(node.children)
        var curNode = lp.consume(kind: SyntaxNodeKind.TryToken)
        let tryKeyWordPos = startPos + lp.offset
        lp.moveOffset(curNode)

        var resourceSpecLParenPos: ?CodePosition = None
        if (let Some(node) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            resourceSpecLParenPos = startPos + lp.offset
            lp.moveOffset(node)
        }
        let sep = SyntaxNodeKind.CommaToken
        while (lp.look(SyntaxNodeKind.VarDecl)) {
            curNode = lp.consume(kind: SyntaxNodeKind.VarDecl)
            if (resourceSpecPropInfo.isNone()) {
                resourceSpecPropInfo = PropInfo(lp.cur - 1, lp.offset)
            }
            lp.moveOffset(curNode)

            if (let Some(node) <- lp.lookAndConsume(sep)) {
                resourceSpecCommasPos.add(startPos + lp.offset)
                lp.moveOffset(node)
            }
        }
        var resourceSpecRParenPos: ?CodePosition = None
        if (let Some(node) <- lp.lookAndConsume(SyntaxNodeKind.RParenToken)) {
            resourceSpecRParenPos = startPos + lp.offset
            lp.moveOffset(node)
        }
        curNode = lp.consume(kind: SyntaxNodeKind.Block)
        let tryBlockPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)

        while (lp.look(SyntaxNodeKind.CatchToken)) {
            curNode = lp.consume()
            catchKeyWordsPos.add(startPos + lp.offset)
            lp.moveOffset(curNode)

            curNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
            catchLParensPos.add(startPos + lp.offset)
            lp.moveOffset(curNode)

            curNode = lp.consume(kind: SyntaxNodeKind.CatchPattern)
            catchPatternsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.moveOffset(curNode)

            curNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
            catchRParensPos.add(startPos + lp.offset)
            lp.moveOffset(curNode)

            if (let Some(blk) <- lp.lookAndConsume(SyntaxNodeKind.Block)) {
                catchBlocksPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
                lp.moveOffset(blk)
            }
        }

        var finallyKeyWordPos = Option<CodePosition>.None
        if (let Some(node) <- lp.lookAndConsume(SyntaxNodeKind.FinallyToken)) {
            finallyKeyWordPos = startPos + lp.offset
            lp.moveOffset(node)

            curNode = lp.lookAndConsume(SyntaxNodeKind.Block)
            finallyBlockPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(curNode)
        }

        let posInfo = TryExprPosInfos(tryKeyWordPos, resourceSpecLParenPos, resourceSpecCommasPos.toArray(),
            resourceSpecRParenPos, catchKeyWordsPos.toArray(), catchLParensPos.toArray(), catchRParensPos.toArray(),
            finallyKeyWordPos)
        let propInfo = TryExprPropInfos(catchBlocksPropInfo.toArray(), catchPatternsPropInfo.toArray(),
            finallyBlockPropInfo, resourceSpecPropInfo, tryBlockPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        TryCatch(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transQuoteTokenExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): QuoteToken {
        let lp = LocalParser(node.children)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        QuoteToken(node, startPos, parent, commentsPropInfos)
    }

    private func transQuoteInterpolationExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): QuoteInterpolationExpr {
        // BNF: DOLLAR LPAREN NL* expression NL* RPAREN
        var exprPropInfo = PropInfo()
        var dollarPos = CodePosition()
        var lParenPos: ?CodePosition = None
        var rParenPos: ?CodePosition = None

        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.DollarToken)) {
            dollarPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            lParenPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        let predictExpr = {
            kind: SyntaxNodeKind => kind.isExpr()
        }
        if (lp.look(predictExpr)) {
            if (let Some(node) <- lp.consume()) {
                exprPropInfo = PropInfo(lp.cur - 1, lp.offset)
                lp.moveOffset(node)
            }
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RParenToken)) {
            rParenPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        let posInfo = QuoteInterpolationExprPosInfos(dollarPos, lParenPos, rParenPos)
        let propInfo = QuoteInterpolationExprPropInfos(exprPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        QuoteInterpolationExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transQuoteExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): QuoteExpr {
        // BNF: QUOTE NL* LPAREN NL* (NL* quoteToken | NL* quoteInterpolate | NL* macroExpression)+ NL* RPAREN
        var tokensOrRefExprPropInfo = ArrayList<PropInfo>()
        var lParenPos = CodePosition()
        var rParenPos = CodePosition()
        var quoteKeyWordPos = CodePosition()
        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.QuoteToken)) {
            quoteKeyWordPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            lParenPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        var findFirst = false
        while (let Some(kind) <- lp.look([SyntaxNodeKind.QuoteTokenExpr, SyntaxNodeKind.QuoteInterpolationExpr])) {
            let node = lp.consume(kind: kind)
            tokensOrRefExprPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.moveOffset(node)
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RParenToken)) {
            rParenPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        let posInfo = QuoteExprPosInfos(quoteKeyWordPos, lParenPos, rParenPos)
        let propInfo = QuoteExprPropInfos(tokensOrRefExprPropInfo.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        QuoteExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transTrailingClosureExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): TrailingClosureExpr {
        var calleePropInfo = PropInfo()
        var trailingLambdaExprPropInfo = PropInfo()
        let lp = LocalParser(node.children)
        if (let Some(v) <- lp.consume()) {
            calleePropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)
        }

        var lParenPos = CodePosition()
        var rParenPos = CodePosition()
        let commasPos = ArrayList<CodePosition>()
        var argumentsPropInfo = PropInfo()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            lParenPos = startPos + lp.offset
            lp.moveOffset(v)
        }
        let sep = SyntaxNodeKind.CommaToken
        var findFirst = false
        while (lp.look(SyntaxNodeKind.Argument)) {
            if (let Some(node) <- lp.consume()) {
                if (!findFirst) {
                    findFirst = true
                    argumentsPropInfo = PropInfo(lp.cur - 1, lp.offset)
                }
                lp.moveOffset(node)
            }
            if (lp.look(sep)) {
                var curNode = lp.consume(kind: sep)
                commasPos.add(startPos + lp.offset)
                lp.moveOffset(curNode)
            }
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.RParenToken)) {
            rParenPos = startPos + lp.offset
            lp.moveOffset(v)
        }

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.Lambda)) {
            trailingLambdaExprPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)
        }
        let posInfo = TrailingClosureExprPosInfos(commasPos.toArray(), lParenPos, rParenPos)
        let propInfo = TrailingClosureExprPropInfos(argumentsPropInfo, calleePropInfo, trailingLambdaExprPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        TrailingClosureExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transUnsafeExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): UnsafeExpr {
        // BNF: Unsafe Block
        var unsafePos = CodePosition()
        let lp = LocalParser(node.children)
        // unsafe
        var curNode = lp.consume(kind: SyntaxNodeKind.UnsafeToken)
        unsafePos = startPos + lp.offset
        lp.moveOffset(curNode)

        var blockPropInfo = PropInfo()
        if (lp.look(SyntaxNodeKind.Block)) {
            curNode = lp.consume(kind: SyntaxNodeKind.Block)
            blockPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(curNode)
        }
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        UnsafeExpr(node, startPos, parent, UnsafeExprPosInfos(unsafePos), UnsafeExprPropInfos(blockPropInfo),
            commentsPropInfos)
    }

    private func transMacroExpandExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): MacroExpandExpr {
        let lp = LocalParser(node.children)
        let (calleePropInfo, macroAttrsPropInfo, macroInputsPropInfo, atPos, lSquarePos, rSquarePos, lParenPos,
            rParenPos) = consumeMacroExpandCommon(lp, startPos)

        let propInfos = MacroExpandExprPropInfos(calleePropInfo, macroAttrsPropInfo, macroInputsPropInfo)
        let posInfos = MacroExpandExprPosInfos(atPos, lSquarePos, rSquarePos, lParenPos, rParenPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        MacroExpandExpr(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transMatchExpr(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): MatchExpr {
        // case 1: match (cond) { cases }
        // case 2: match { cases }
        var selectorPropInfo: ?PropInfo = None
        var matchKeyWordPos = CodePosition()
        var selectorLParenPos: ?CodePosition = None
        var selectorRParenPos: ?CodePosition = None
        var matchCasesLCurlPos = CodePosition()
        var matchCasesRCurlPos = CodePosition()
        let lp = LocalParser(node.children)
        // match
        var curNode = lp.consume(kind: SyntaxNodeKind.MatchToken)
        matchKeyWordPos = startPos + lp.offset
        lp.moveOffset(curNode)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            // case 1:  (cond)
            selectorLParenPos = startPos + lp.offset
            lp.moveOffset(v)
            curNode = lp.consume()
            selectorPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(curNode)
            curNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
            selectorRParenPos = startPos + lp.offset
            lp.moveOffset(curNode)
        }
        // case 1 & case 2:  { cases }
        curNode = lp.consume(kind: SyntaxNodeKind.LCurlToken)
        matchCasesLCurlPos = startPos + lp.offset
        lp.moveOffset(curNode)
        var matchCasesPropInfo = ArrayList<PropInfo>()
        var findFirstCase = false
        while (!lp.look(SyntaxNodeKind.RCurlToken)) {
            curNode = lp.consume()
            matchCasesPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.moveOffset(curNode)
        }
        curNode = lp.consume(kind: SyntaxNodeKind.RCurlToken)
        matchCasesRCurlPos = startPos + lp.offset
        lp.moveOffset(curNode)
        let posInfo = MatchExprPosInfos(matchKeyWordPos, selectorLParenPos, selectorRParenPos, matchCasesLCurlPos,
            matchCasesRCurlPos)
        let propInfo = MatchExprPropInfos(matchCasesPropInfo.toArray(), selectorPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        MatchExpr(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transMatchCase(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): MatchCase {
        // BNF: case patterns patternGuard? => caseBody
        //      | case expr => caseBody
        var bodyPropInfo = PropInfo()
        var caseCondPropInfo: ?PropInfo = None
        var patternGuardCondPropInfo: ?PropInfo = None
        var patternsPropInfo = ArrayList<PropInfo>()
        var casePos = CodePosition()
        var bitOrsPos = ArrayList<CodePosition>()
        var wherePos: ?CodePosition = None
        var doubleArrowPos = CodePosition()
        let lp = LocalParser(node.children)
        // case 
        let caseNode = lp.consume(kind: SyntaxNodeKind.CaseToken)
        casePos = startPos + lp.offset
        lp.moveOffset(caseNode)

        let predictPattern = {
            kind: SyntaxNodeKind => kind.isPattern()
        }
        if (lp.look(predictPattern)) {
            // case 1: pattern (| pattern)*, maybe more patterns such as 1 | 2 | 3
            while (!lp.look(SyntaxNodeKind.DoubleArrowToken) && !lp.look(SyntaxNodeKind.WhereToken)) {
                let patternNode = lp.consume()
                patternsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
                lp.moveOffset(patternNode)

                if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.BitOrToken)) {
                    bitOrsPos.add(startPos + lp.offset)
                    lp.moveOffset(v)
                }
            }
            // patternGuard
            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.WhereToken)) {
                wherePos = startPos + lp.offset
                lp.moveOffset(v)
                let patternGuardNode = lp.consume()
                patternGuardCondPropInfo = PropInfo(lp.cur - 1, lp.offset)
                lp.moveOffset(patternGuardNode)
            }
        } else {
            // case 2: expr
            let caseCondNode = lp.consume()
            caseCondPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(caseCondNode)
        }
        // =>
        let doubleArrowNode = lp.consume(kind: SyntaxNodeKind.DoubleArrowToken)
        doubleArrowPos = startPos + lp.offset
        lp.moveOffset(doubleArrowNode)
        // case body
        if (let Some(bodyImpl) <- lp.consume(kind: SyntaxNodeKind.MatchCaseBody) && let Some(caseBody) <- (bodyImpl as NonTerminal)) {
            bodyPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(bodyImpl)
        }

        let propInfos = MatchCasePropInfos(bodyPropInfo, caseCondPropInfo, patternGuardCondPropInfo,
            patternsPropInfo.toArray())
        let posInfos = MatchCasePosInfos(casePos, bitOrsPos.toArray(), wherePos, doubleArrowPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        MatchCase(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transConstPattern(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ConstPattern {
        // BNF: literalConst
        let lp = LocalParser(node.children)
        let litExprNode = lp.consume()
        let exprPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(litExprNode)

        let propInfos = ConstPatternPropInfos(exprPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ConstPattern(node, startPos, parent, propInfos, commentsPropInfos)
    }

    private func transWildcardPattern(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): WildcardPattern {
        let lp = LocalParser(node.children)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        WildcardPattern(node, startPos, parent, commentsPropInfos)
    }

    private func transVarBindingPattern(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): VarPattern {
        // BNF: identifier
        let lp = LocalParser(node.children)
        var name: String = ""
        if (let Some(id) <- lp.consume(kind: SyntaxNodeKind.IdentToken)) {
            name = getValue(id)
        }
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        VarPattern(node, startPos, parent, name, commentsPropInfos)
    }

    private func transVarOrEnumPattern(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): VarOrEnumPattern {
        // BNF: identifier with enum pattern context
        let lp = LocalParser(node.children)
        var name: String = ""
        if (let Some(id) <- lp.consume(kind: SyntaxNodeKind.IdentToken)) {
            name = getValue(id)
        }
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        VarOrEnumPattern(node, startPos, parent, name, commentsPropInfos)
    }

    private func transTypePattern(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): TypePattern {
        // BNF: subPattern : type, subPattern must be var binding pattern or wild
        let lp = LocalParser(node.children)
        var subPatternPropInfo = PropInfo()
        var patternTyPropInfo = PropInfo()
        var colonPos = CodePosition()
        if (let Some(kind) <- lp.look([SyntaxNodeKind.VarBindingPattern, SyntaxNodeKind.WildcardPattern])) {
            let subPatternNode = lp.consume(kind: kind)
            subPatternPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(subPatternNode)

            let colonNode = lp.consume(kind: SyntaxNodeKind.ColonToken)
            colonPos = startPos + lp.offset
            lp.moveOffset(colonNode)

            let patternTyNode = lp.consume()
            patternTyPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(patternTyNode)
        }
        let propInfos = TypePatternPropInfos(patternTyPropInfo, subPatternPropInfo)
        let posInfos = TypePatternPosInfos(colonPos)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        TypePattern(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transEnumPattern(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): EnumPattern {
        // BNF: (compositeType DOT)? identifier (LPAREN pattern (COMMA pattern)* RPAREN)?
        let lp = LocalParser(node.children)
        var enumTypePropInfo: ?PropInfo = None
        var enumConstructorPropInfo = PropInfo()
        var dotPos: Option<CodePosition> = None
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.CompositeType)) {
            enumTypePropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)

            let dotNode = lp.consume(kind: SyntaxNodeKind.DotToken)
            dotPos = startPos + lp.offset
            lp.moveOffset(dotNode)
        }

        let refNode = lp.consume(kind: SyntaxNodeKind.RefExpr)
        enumConstructorPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(refNode)

        var lParenPos: Option<CodePosition> = None
        var rParenPos: Option<CodePosition> = None
        var commasPos = ArrayList<CodePosition>()
        var subPatternsPropInfo = ArrayList<PropInfo>()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LParenToken)) {
            lParenPos = startPos + lp.offset
            lp.moveOffset(v)
            while (!lp.look(SyntaxNodeKind.RParenToken)) {
                let patternNode = lp.consume()
                subPatternsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
                lp.moveOffset(patternNode)
                if (let Some(b) <- lp.lookAndConsume(SyntaxNodeKind.CommaToken)) {
                    commasPos.add(startPos + lp.offset)
                    lp.moveOffset(b)
                }
            }
            let rParenNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
            rParenPos = startPos + lp.offset
            lp.moveOffset(rParenNode)
        }

        let propInfos = EnumPatternPropInfos(enumConstructorPropInfo, enumTypePropInfo, subPatternsPropInfo.toArray())
        let posInfos = EnumPatternPosInfos(dotPos, lParenPos, rParenPos, commasPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        EnumPattern(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transTuplePattern(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): TuplePattern {
        // BNF: (pattern (COMMA pattern)+)
        let lp = LocalParser(node.children)
        let lParenNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
        let lParenPos = startPos + lp.offset
        lp.moveOffset(lParenNode)

        let subPatternsPropInfo = ArrayList<PropInfo>()
        let commasPos = ArrayList<CodePosition>()
        while (!lp.look(SyntaxNodeKind.RParenToken)) {
            let patternNode = lp.consume()
            subPatternsPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
            lp.moveOffset(patternNode)

            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.CommaToken)) {
                commasPos.add(startPos + lp.offset)
                lp.moveOffset(v)
            }
        }
        let rParenNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        let rParenPos = startPos + lp.offset
        lp.moveOffset(rParenNode)

        let propInfos = TuplePatternPropInfos(subPatternsPropInfo.toArray())
        let posInfos = TuplePatternPosInfos(lParenPos, rParenPos, commasPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        TuplePattern(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    private func transCatchPattern(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): CatchPattern {
        // BNF: wildcardPattern |
        //      (wildcardPattern | identifier) COLON type (BITOR type)*
        let lp = LocalParser(node.children)
        let patternNode = lp.consume()
        let patternPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(patternNode)

        let exceptionTypesPropInfo = ArrayList<PropInfo>()
        var colonPos: ?CodePosition = None
        let bitOrsPos = ArrayList<CodePosition>()
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.ColonToken)) {
            colonPos = startPos + lp.offset
            lp.moveOffset(v)
            while (!lp.isEnd()) {
                if (let Some(typeNode) <- lp.tryConsume()) {
                    exceptionTypesPropInfo.add(PropInfo(lp.cur - 1, lp.offset))
                    lp.moveOffset(typeNode)
                }

                if (let Some(b) <- lp.lookAndConsume(SyntaxNodeKind.BitOrToken)) {
                    bitOrsPos.add(startPos + lp.offset)
                    lp.moveOffset(b)
                }
            }
        }

        let propInfos = CatchPatternPropInfos(exceptionTypesPropInfo.toArray(), patternPropInfo)
        let posInfos = CatchPatternPosInfos(colonPos, bitOrsPos.toArray())
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        CatchPattern(node, startPos, parent, posInfos, propInfos, commentsPropInfos)
    }

    // TypeAnnotation
    private func transAtomicType(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): AtomicType {
        let lp = LocalParser(node.children)
        let kindNode = lp.consume().getOrThrow()
        let kind = match (kindNode.kind) {
            case SyntaxNodeKind.BooleanToken => AtomicTypeKind.BoolType
            case SyntaxNodeKind.Float16Token => AtomicTypeKind.Float16Type
            case SyntaxNodeKind.Float32Token => AtomicTypeKind.Float32Type
            case SyntaxNodeKind.Float64Token => AtomicTypeKind.Float64Type
            case SyntaxNodeKind.Int16Token => AtomicTypeKind.Int16Type
            case SyntaxNodeKind.Int32Token => AtomicTypeKind.Int32Type
            case SyntaxNodeKind.Int64Token => AtomicTypeKind.Int64Type
            case SyntaxNodeKind.Int8Token => AtomicTypeKind.Int8Type
            case SyntaxNodeKind.IntNativeToken => AtomicTypeKind.IntNativeType
            case SyntaxNodeKind.NothingToken => AtomicTypeKind.NothingType
            case SyntaxNodeKind.RuneToken => AtomicTypeKind.RuneType
            case SyntaxNodeKind.UInt16Token => AtomicTypeKind.UInt16Type
            case SyntaxNodeKind.UInt32Token => AtomicTypeKind.UInt32Type
            case SyntaxNodeKind.UInt64Token => AtomicTypeKind.UInt64Type
            case SyntaxNodeKind.UInt8Token => AtomicTypeKind.UInt8Type
            case SyntaxNodeKind.UIntNativeToken => AtomicTypeKind.UIntNativeType
            case SyntaxNodeKind.UnitToken => AtomicTypeKind.UnitType
            case SyntaxNodeKind.ThisTypeToken => AtomicTypeKind.ThisType
            case _ => throw Exception("ParseException: This SyntaxNode kind is not Atomic type.")
        }
        lp.moveOffset(kindNode)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        AtomicType(node, startPos, parent, kind, commentsPropInfos)
    }

    private func transCompositeType(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): CompositeType {
        // BNF: (identifier NL* DOT NL*)* identifier ( NL* typeArguments)?
        let lp = LocalParser(node.children)
        // (identifier DOT)* identifier
        let (ids, identPos, dotsPos) = lp.consumeListOfPos(startPos, kind: SyntaxNodeKind.IdentToken,
            sep: SyntaxNodeKind.DotToken)

        var lAngelPos: ?CodePosition = None
        var commaPos = ArrayList<CodePosition>()
        var rAngelPos: ?CodePosition = None
        var typeArgumentsPropInfo: ?PropInfo = None
        if (lp.look(SyntaxNodeKind.TypeArguments) && let Some(v) <- lp.consume()) {
            // LT type (COMMA type)* GT
            typeArgumentsPropInfo = PropInfo(lp.cur - 1, lp.offset)
            (lAngelPos, commaPos, rAngelPos) = transTypeArguments(v, startPos + lp.offset)
        }
        let posInfo = CompositeTypePosInfos(lAngelPos, rAngelPos, commaPos.toArray(), dotsPos.toArray(),
            identPos.toArray())
        let propInfo = CompositeTypePropInfos(typeArgumentsPropInfo)
        let size = ids.size
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        CompositeType(node, startPos, parent, posInfo, propInfo, getValue(ids[size - 1]),
            Array(size - 1, {i => getValue(ids[i])}), commentsPropInfos)
    }

    private func transTupleType(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): TupleType {
        // BNF: LPAREN NL* type (NL* COMMA NL* type)+ NL* COMMA? NL* RPAREN
        // BNF: LPAREN NL* identifier NL* COLON NL* type (NL* COMMA NL* identifier NL* COLON NL* type)+ NL* COMMA? NL* RPAREN
        let lp = LocalParser(node.children)
        let lParenPos = startPos
        lp.moveOffset(lp.consume(kind: SyntaxNodeKind.LParenToken))

        var labels = ArrayList<String>()
        var labelsPos = ArrayList<CodePosition>()
        var colonPos = ArrayList<CodePosition>()
        var commaPos = ArrayList<CodePosition>()

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            labels.add(getValue(v))
            labelsPos.add(startPos + lp.offset)
            lp.moveOffset(v)

            let curNode = lp.consume(kind: SyntaxNodeKind.ColonToken)
            colonPos.add(startPos + lp.offset)
            lp.moveOffset(curNode)
        }
        var elementsPropInfo = PropInfo()
        var curNode = lp.consume()
        elementsPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)
        while (lp.look(SyntaxNodeKind.CommaToken)) {
            commaPos.add(consumeCommaPos(lp, startPos))
            consumeOptionalTypeLabel(lp, startPos, labels, labelsPos, colonPos)
            lp.moveOffset(lp.consume())
        }

        curNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        let rParenPos = startPos + lp.offset

        let posInfo = TupleTypePosInfos(lParenPos, rParenPos, commaPos.toArray(), labelsPos.toArray(),
            colonPos.toArray())
        let propInfo = TupleTypePropInfos(elementsPropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        TupleType(node, startPos, parent, posInfo, propInfo, labels.toArray(), commentsPropInfos)
    }

    private func transFuncType(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): FuncType {
        // BNF: arrowParameters NL* ARROW NL* type
        let lp = LocalParser(node.children)
        var curNode = lp.consume(kind: SyntaxNodeKind.LParenToken)
        let lParenPos = startPos + lp.offset
        lp.moveOffset(curNode)

        var labels = ArrayList<String>()
        var labelsPos = ArrayList<CodePosition>()
        var colonPos = ArrayList<CodePosition>()
        var commaPos = ArrayList<CodePosition>()

        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.IdentToken)) {
            labels.add(getValue(v))
            labelsPos.add(startPos + lp.offset)
            lp.moveOffset(v)
            curNode = lp.consume(kind: SyntaxNodeKind.ColonToken)
            colonPos.add(startPos + lp.offset)
            lp.moveOffset(curNode)
        }
        var paramTypesPropInfo: ?PropInfo = None
        if (!lp.look(SyntaxNodeKind.RParenToken)) {
            curNode = lp.consume()
            paramTypesPropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(curNode)
        }
        while (lp.look(SyntaxNodeKind.CommaToken)) {
            commaPos.add(consumeCommaPos(lp, startPos))
            consumeOptionalTypeLabel(lp, startPos, labels, labelsPos, colonPos)
            lp.moveOffset(lp.consume())
        }

        curNode = lp.consume(kind: SyntaxNodeKind.RParenToken)
        let rParenPos = startPos + lp.offset
        lp.moveOffset(curNode)
        curNode = lp.consume(kind: SyntaxNodeKind.ArrowToken)
        let arrowPos = startPos + lp.offset
        lp.moveOffset(curNode)
        let retType = lp.consume()
        let retTypePropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(retType)

        let posInfo = FuncTypePosInfos(lParenPos, rParenPos, commaPos.toArray(), arrowPos, labelsPos.toArray(),
            colonPos.toArray())
        let propInfo = FuncTypePropInfos(paramTypesPropInfo, retTypePropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        FuncType(node, startPos, parent, posInfo, propInfo, labels.toArray(), commentsPropInfos)
    }

    private func transVArrayType(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): VArrayType {
        let lp = LocalParser(node.children)
        let varrayPos = startPos
        lp.moveOffset(lp.consume(kind: SyntaxNodeKind.VArrayToken))
        var curNode = lp.consume(kind: SyntaxNodeKind.LtToken)
        let lAngelPos = startPos + lp.offset
        lp.moveOffset(curNode)

        curNode = lp.consume()
        let elementPropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)

        curNode = lp.consume(kind: SyntaxNodeKind.CommaToken)
        let commaPos = startPos + lp.offset
        lp.moveOffset(curNode)

        curNode = lp.consume(kind: SyntaxNodeKind.DollarToken)
        let dollarPos = startPos + lp.offset
        lp.moveOffset(curNode)

        var sizePropInfo = PropInfo()
        if (let Some(v) <- lp.lookAndConsume(ValuedLiteral)) {
            sizePropInfo = PropInfo(lp.cur - 1, lp.offset)
            lp.moveOffset(v)
        }

        curNode = lp.consume(kind: SyntaxNodeKind.GtToken)
        let rAngelPos = startPos + lp.offset
        lp.moveOffset(curNode)

        let posInfo = VArrayTypePosInfos(varrayPos, lAngelPos, rAngelPos, dollarPos, commaPos)
        let propInfo = VArrayTypePropInfos(elementPropInfo, sizePropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        VArrayType(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transTypeArguments(node: SyntaxNodeImpl, startPos: CodePosition): (CodePosition, ArrayList<CodePosition>, 
        CodePosition) {
        // BNF: identifier (NL* typeArguments)?
        var lAnglePos = CodePosition()
        var rAnglePos = CodePosition()
        let commasPos = ArrayList<CodePosition>()

        let lp = LocalParser((node as NonTerminal).getOrThrow().children)
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.LtToken)) {
            lAnglePos = startPos
            lp.offset.move(v.offset)
        }
        let sep = SyntaxNodeKind.CommaToken
        let predictType = {
            kind: SyntaxNodeKind => kind.isTypeAnnotation()
        }
        while (lp.look(predictType)) {
            lp.offset.move(lp.consume().getOrThrow().offset)
            if (let Some(v) <- lp.lookAndConsume(sep)) {
                commasPos.add(startPos + lp.offset)
                lp.offset.move(v.offset)
            }
        }
        if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.GtToken)) {
            rAnglePos = startPos + lp.offset
        }
        return (lAnglePos, commasPos, rAnglePos)
    }

    private func transGenericParams(node: SyntaxNodeImpl, startPos: CodePosition) {
        // BNF :LT GenericParam (Comma GenericParam)* GT
        let lp = LocalParser(((node as NonTerminal).getOrThrow()).children)
        let ltNode = lp.consume(kind: SyntaxNodeKind.LtToken)
        let lAnglePos = startPos + lp.offset
        lp.offset.move(ltNode?.offset)
        let commasPos = ArrayList<CodePosition>()
        while (!lp.look(SyntaxNodeKind.GtToken)) {
            lp.offset.move(lp.consume().getOrThrow().offset)
            if (let Some(v) <- lp.lookAndConsume(SyntaxNodeKind.CommaToken)) {
                commasPos.add(startPos + lp.offset)
                lp.offset.move(v.offset)
            }
        }
        let rAnglePos = startPos + lp.offset
        (lAnglePos, commasPos.toArray(), rAnglePos)
    }

    private func transParenType(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): ParenType {
        // BNF: LPAREN NL* type NL* RPAREN
        let lp = LocalParser(node.children)
        let lParenPos = startPos
        lp.moveOffset(lp.consume(kind: SyntaxNodeKind.LParenToken))
        let curNode = lp.consume()
        let subTypePropInfo = PropInfo(lp.cur - 1, lp.offset)
        lp.moveOffset(curNode)
        lp.consume(kind: SyntaxNodeKind.RParenToken)
        let rParenPos = startPos + lp.offset

        let posInfo = ParenTypePosInfos(lParenPos, rParenPos)
        let propInfo = ParenTypePropInfos(subTypePropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        ParenType(node, startPos, parent, posInfo, propInfo, commentsPropInfos)
    }

    private func transPrefixType(node: NonTerminal, startPos: CodePosition, parent: ?SyntaxTreeNode): PrefixType {
        // BNF: prefixTypeOperator type
        let lp = LocalParser(node.children)
        var prefixTypeOpKind = PrefixTypeOpKind.Quest

        let prefixTypeOpPos = startPos
        let prefix = lp.consume()
        if (let Some(pre) <- prefix) {
            prefixTypeOpKind = match (pre.kind) {
                case SyntaxNodeKind.QuestToken => PrefixTypeOpKind.Quest
                case _ =>
                    fail("unsupported prefixTypeOperator")
                    PrefixTypeOpKind.Quest
            }
            lp.moveOffset(pre)
        }
        lp.consume()
        let baseTypePropInfo = PropInfo(lp.cur - 1, lp.offset)

        let posInfo = PrefixTypePosInfos(prefixTypeOpPos)
        let propInfo = PrefixTypePropInfos(baseTypePropInfo)
        lp.consumeToEnd()
        let commentsPropInfos = lp.commentGroupListPropInfos.toArray()
        PrefixType(node, startPos, parent, posInfo, propInfo, prefixTypeOpKind, commentsPropInfos)
    }
}

class LocalParser {
    var cur: Int64 = 0
    private let end: Int64
    let offset: Offset
    let commentGroupListPropInfos: ArrayList<PropInfo> = ArrayList<PropInfo>()

    LocalParser(let nodes: Array<SyntaxNodeImpl>) {
        end = nodes.size
        offset = Offset()
    }

    init(nodes: Array<SyntaxNodeImpl>, off: Offset, index!: Int64 = 0) {
        this.nodes = nodes
        end = nodes.size
        offset = Offset(off.line, off.column)
        cur = index
    }

    // try look up one token, and consume it
    func lookAndConsume(kind: SyntaxNodeKind): ?SyntaxNodeImpl {
        var ret: ?SyntaxNodeImpl = None
        if (look(kind)) {
            return consume()
        }
        return ret
    }

    // consume one token non white space token.
    func consume(): ?SyntaxNodeImpl {
        var ret: ?SyntaxNodeImpl = None
        consumeWhitespace()
        if (!isEnd()) {
            ret = nodes[cur]
            cur++
        } else {
            fail("expect node but empty")
        }
        ret
    }

    // consume one node, can be empty.
    func tryConsume(): ?SyntaxNodeImpl {
        var ret: ?SyntaxNodeImpl = None
        consumeWhitespace()
        if (!isEnd()) {
            ret = nodes[cur]
            cur++
        }
        ret
    }

    // consume one token with kind.
    func consume(kind!: SyntaxNodeKind): ?SyntaxNodeImpl {
        var ret: ?SyntaxNodeImpl = None
        consumeWhitespace()
        if (!isEnd()) {
            if (nodes[cur].kind == kind) {
                ret = nodes[cur]
                cur++
            } else {
                fail("expect ${kind} but get ${nodes[cur].kind}")
            }
        } else {
            fail("expect node but empty")
        }
        ret
    }

    // consume list: node sep? node -> [node, node] when node with kind and record pos.
    func consumeListOfPos(startPos: CodePosition, kind!: SyntaxNodeKind, sep!: ?SyntaxNodeKind = None): (ArrayList<SyntaxNodeImpl>, 
        ArrayList<CodePosition>, ArrayList<CodePosition>) {
        var (ret, pos1, pos2) = (ArrayList<SyntaxNodeImpl>(), ArrayList<CodePosition>(), ArrayList<CodePosition>())
        while (look(kind)) {
            if (let Some(node) <- consume(kind: kind)) {
                ret.add(node)
                pos1.add(startPos + offset)
                moveOffset(node)
            }
            if (let Some(sp) <- sep && look(sp)) {
                let spp = consume(kind: sp)
                pos2.add(startPos + offset)
                moveOffset(spp)
            }
        }
        (ret, pos1, pos2)
    }

    // look ahead one token with kind.
    func look(kind: SyntaxNodeKind): Bool {
        if (let Some(top) <- peek() && top.kind == kind) {
            true
        } else {
            false
        }
    }

    // look ahead one token with multi kinds.
    func look(kinds: Array<SyntaxNodeKind>): ?SyntaxNodeKind {
        if (let Some(top) <- peek()) {
            for (kind in kinds) {
                if (top.kind == kind) {
                    return kind
                }
            }
        }
        return None
    }

    // look ahead one token when predict(token) is true.
    func look(predict: (SyntaxNodeKind) -> Bool): Bool {
        if (let Some(top) <- peek() && predict(top.kind)) {
            true
        } else {
            false
        }
    }

    func isEnd(): Bool {
        cur >= end
    }

    private func peek(): ?SyntaxNodeImpl {
        consumeWhitespace()
        if (!isEnd()) {
            nodes[cur]
        } else {
            None
        }
    }

    func moveOffset(node: ?SyntaxNodeImpl) {
        offset.moveNode(node)
    }

    func consumeWhitespace() {
        while (!isEnd()) {
            if (nodes[cur].kind.isWhitespace()) {
                moveOffset(nodes[cur])
                cur++
            } else if (nodes[cur].kind == CommentGroupList) {
                commentGroupListPropInfos.add(PropInfo(cur, offset))
                moveOffset(nodes[cur])
                cur++
            } else {
                break
            }
        }
    }

    func consumeToEnd() {
        while (!isEnd()) {
            moveOffset(tryConsume())
        }
    }
}

func cast<T>(node: ?SyntaxTreeNode): ?T where T <: SyntaxTreeNode {
    if (let Some(value) <- node) {
        value as T
    } else {
        None
    }
}

func getValue(s: SyntaxNodeImpl): String {
    if (let Some(vt) <- (s as ValuedTerminal)) {
        vt.value
    } else {
        ""
    }
}