Code review comment: I think we don't need `parseXMLStepExtendList`method at all :)  Reason is `.<`, `.` and `[` are already handled by parseExpressionRhs method. So xml-step-expression node always have only `lhsExpr` and `xmlStepStart`

Code under review:
private boolean isServiceDeclStart(ParserRuleContext currentContext, int lookahead) {
        
        switch (peek(lookahead + 1).kind) {
            case IDENTIFIER_TOKEN:
                SyntaxKind tokenAfterIdentifier = peek(lookahead + 2).kind;
                switch (tokenAfterIdentifier) {
                    case ON_KEYWORD: 
                    case OPEN_BRACE_TOKEN: 
                        return true;
                    case EQUAL_TOKEN: 
                    case SEMICOLON_TOKEN: 
                    case QUESTION_MARK_TOKEN: 
                        return false;
                    default:
                        
                        return false;
                }
            case ON_KEYWORD:
                
                
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse listener declaration, given the qualifier.
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the listener declaration
     * @return Parsed node
     */
    private STNode parseListenerDeclaration(STNode metadata, STNode qualifier) {
        startContext(ParserRuleContext.LISTENER_DECL);
        STNode listenerKeyword = parseListenerKeyword();
        STNode typeDesc = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_BEFORE_IDENTIFIER);
        STNode variableName = parseVariableName();
        STNode equalsToken = parseAssignOp();
        STNode initializer = parseExpression();
        STNode semicolonToken = parseSemicolon();
        endContext();
        return STNodeFactory.createListenerDeclarationNode(metadata, qualifier, listenerKeyword, typeDesc, variableName,
                equalsToken, initializer, semicolonToken);
    }

    /**
     * Parse listener keyword.
     *
     * @return Parsed node
     */
    private STNode parseListenerKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.LISTENER_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.LISTENER_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse constant declaration, given the qualifier.
     * <p>
     * <code>module-const-decl := metadata [public] const [type-descriptor] identifier = const-expr ;</code>
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the listener declaration
     * @return Parsed node
     */
    private STNode parseConstantDeclaration(STNode metadata, STNode qualifier) {
        startContext(ParserRuleContext.CONSTANT_DECL);
        STNode constKeyword = parseConstantKeyword();
        STNode constDecl = parseConstDecl(metadata, qualifier, constKeyword);
        endContext();
        return constDecl;
    }

    /**
     * Parse the components that follows after the const keyword of a constant declaration.
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the constant decl
     * @param constKeyword Const keyword
     * @return Parsed node
     */
    private STNode parseConstDecl(STNode metadata, STNode qualifier, STNode constKeyword) {
        STToken nextToken = peek();
        return parseConstDeclFromType(nextToken.kind, metadata, qualifier, constKeyword);
    }

    private STNode parseConstDeclFromType(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
                                          STNode constKeyword) {
        switch (nextTokenKind) {
            case ANNOTATION_KEYWORD:
                switchContext(ParserRuleContext.ANNOTATION_DECL);
                return parseAnnotationDeclaration(metadata, qualifier, constKeyword);
            case IDENTIFIER_TOKEN:
                return parseConstantDeclWithOptionalType(metadata, qualifier, constKeyword);
            default:
                if (isTypeStartingToken(nextTokenKind)) {
                    break;
                }
                STToken token = peek();
                Solution solution =
                        recover(token, ParserRuleContext.CONST_DECL_TYPE, metadata, qualifier, constKeyword);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseConstDeclFromType(solution.tokenKind, metadata, qualifier, constKeyword);
        }

        STNode typeDesc = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_BEFORE_IDENTIFIER);
        STNode variableName = parseVariableName();
        STNode equalsToken = parseAssignOp();
        STNode initializer = parseExpression();
        STNode semicolonToken = parseSemicolon();
        return STNodeFactory.createConstantDeclarationNode(metadata, qualifier, constKeyword, typeDesc, variableName,
                equalsToken, initializer, semicolonToken);
    }

    private STNode parseConstantDeclWithOptionalType(STNode metadata, STNode qualifier, STNode constKeyword) {
        STNode varNameOrTypeName = parseStatementStartIdentifier();
        STNode constDecl = parseConstantDeclRhs(metadata, qualifier, constKeyword, varNameOrTypeName);
        return constDecl;
    }

    /**
     * Parse the component that follows the first identifier in a const decl. The identifier
     * can be either the type-name (a user defined type) or the var-name there the type-name
     * is not present.
     *
     * @param qualifier Qualifier that precedes the constant decl
     * @param constKeyword Const keyword
     * @param typeOrVarName Identifier that follows the const-keywoord
     * @return Parsed node
     */
    private STNode parseConstantDeclRhs(STNode metadata, STNode qualifier, STNode constKeyword, STNode typeOrVarName) {
        STToken token = peek();
        return parseConstantDeclRhs(token.kind, metadata, qualifier, constKeyword, typeOrVarName);
    }

    private STNode parseConstantDeclRhs(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
                                        STNode constKeyword, STNode typeOrVarName) {
        STNode type;
        STNode variableName;
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN:
                type = typeOrVarName;
                variableName = parseVariableName();
                break;
            case EQUAL_TOKEN:
                variableName = ((STSimpleNameReferenceNode) typeOrVarName).name; 
                type = STNodeFactory.createEmptyNode();
                break;
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.CONST_DECL_RHS, metadata, qualifier, constKeyword,
                        typeOrVarName);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseConstantDeclRhs(solution.tokenKind, metadata, qualifier, constKeyword, typeOrVarName);
        }

        STNode equalsToken = parseAssignOp();
        STNode initializer = parseExpression();
        STNode semicolonToken = parseSemicolon();
        return STNodeFactory.createConstantDeclarationNode(metadata, qualifier, constKeyword, type, variableName,
                equalsToken, initializer, semicolonToken);
    }

    /**
     * Parse const keyword.
     *
     * @return Parsed node
     */
    private STNode parseConstantKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.CONST_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.CONST_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse nil type descriptor.
     * <p>
     * <code>nil-type-descriptor :=  ( ) </code>
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseNilTypeDescriptor() {
        startContext(ParserRuleContext.NIL_TYPE_DESCRIPTOR);
        STNode openParenthesisToken = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
        STNode closeParenthesisToken = parseCloseParenthesis();
        endContext();

        return STNodeFactory.createNilTypeDescriptorNode(openParenthesisToken, closeParenthesisToken);
    }

    /**
     * Parse typeof expression.
     * <p>
     * <code>
     * typeof-expr := typeof expression
     * </code>
     *
     * @param isRhsExpr
     * @return Typeof expression node
     */
    private STNode parseTypeofExpression(boolean isRhsExpr) {
        STNode typeofKeyword = parseTypeofKeyword();

        
        
        STNode expr = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false);
        return STNodeFactory.createTypeofExpressionNode(typeofKeyword, expr);
    }

    /**
     * Parse typeof-keyword.
     *
     * @return Typeof-keyword node
     */
    private STNode parseTypeofKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.TYPEOF_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.TYPEOF_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse optional type descriptor.
     * <p>
     * <code>optional-type-descriptor := type-descriptor ? </code>
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseOptionalTypeDescriptor(STNode typeDescriptorNode) {
        startContext(ParserRuleContext.OPTIONAL_TYPE_DESCRIPTOR);
        STNode questionMarkToken = parseQuestionMark();
        endContext();

        return STNodeFactory.createOptionalTypeDescriptorNode(typeDescriptorNode, questionMarkToken);
    }

    /**
     * Parse unary expression.
     * <p>
     * <code>
     * unary-expr := + expression | - expression | ~ expression | ! expression
     * </code>
     *
     * @param isRhsExpr
     * @return Unary expression node
     */
    private STNode parseUnaryExpression(boolean isRhsExpr) {
        STNode unaryOperator = parseUnaryOperator();

        
        
        STNode expr = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false);
        return STNodeFactory.createUnaryExpressionNode(unaryOperator, expr);
    }

    /**
     * Parse unary operator.
     * <code>UnaryOperator := + | - | ~ | !</code>
     *
     * @return Parsed node
     */
    private STNode parseUnaryOperator() {
        STToken token = peek();
        if (isUnaryOperator(token.kind)) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.UNARY_OPERATOR);
            return sol.recoveredNode;
        }
    }

    /**
     * Check whether the given token kind is a unary operator.
     *
     * @param kind STToken kind
     * @return <code>true</code> if the token kind refers to a unary operator. <code>false</code> otherwise
     */
    private boolean isUnaryOperator(SyntaxKind kind) {
        switch (kind) {
            case PLUS_TOKEN:
            case MINUS_TOKEN:
            case NEGATION_TOKEN:
            case EXCLAMATION_MARK_TOKEN:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse array type descriptor.
     * <p>
     * <code>
     * array-type-descriptor := member-type-descriptor [ [ array-length ] ]
     * member-type-descriptor := type-descriptor
     * array-length :=
     *    int-literal
     *    | constant-reference-expr
     *    | inferred-array-length
     * inferred-array-length := *
     * </code>
     * </p>
     *
     * @param memberTypeDesc
     *
     * @return Parsed Node
     */
    private STNode parseArrayTypeDescriptor(STNode memberTypeDesc) {
        startContext(ParserRuleContext.ARRAY_TYPE_DESCRIPTOR);
        STNode openBracketToken = parseOpenBracket();
        STNode arrayLengthNode = parseArrayLength();
        STNode closeBracketToken = parseCloseBracket();
        endContext();
        return STNodeFactory.createArrayTypeDescriptorNode(memberTypeDesc, openBracketToken, arrayLengthNode,
                closeBracketToken);
    }

    /**
     * Parse array length.
     * <p>
     * <code>
     *     array-length :=
     *    int-literal
     *    | constant-reference-expr
     *    | inferred-array-length
     * constant-reference-expr := variable-reference-expr
     * </code>
     * </p>
     *
     * @return Parsed array length
     */
    private STNode parseArrayLength() {
        STToken token = peek();
        switch (token.kind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case ASTERISK_TOKEN:
                return parseBasicLiteral();
            case CLOSE_BRACKET_TOKEN:
                return STNodeFactory.createEmptyNode();
            
            case IDENTIFIER_TOKEN:
                return parseQualifiedIdentifier(ParserRuleContext.ARRAY_LENGTH);
            default:
                Solution sol = recover(token, ParserRuleContext.ARRAY_LENGTH);
                return sol.recoveredNode;
        }
    }

    /**
     * Parse annotations.
     * <p>
     * <i>Note: In the ballerina spec ({@link https:
     * annotations-list is specified as one-or-more annotations. And the usage is marked as
     * optional annotations-list. However, for the consistency of the tree, here we make the
     * annotation-list as zero-or-more annotations, and the usage is not-optional.</i>
     * <p>
     * <code>annots := annotation*</code>
     *
     * @return Parsed node
     */
    private STNode parseAnnotations() {
        STToken nextToken = peek();
        return parseAnnotations(nextToken.kind);
    }

    private STNode parseAnnotations(SyntaxKind nextTokenKind) {
        startContext(ParserRuleContext.ANNOTATIONS);
        List<STNode> annotList = new ArrayList<>();
        while (nextTokenKind == SyntaxKind.AT_TOKEN) {
            annotList.add(parseAnnotation());
            nextTokenKind = peek().kind;
        }

        endContext();
        return STNodeFactory.createNodeList(annotList);
    }

    /**
     * Parse annotation attachment.
     * <p>
     * <code>annotation := @ annot-tag-reference annot-value</code>
     *
     * @return Parsed node
     */
    private STNode parseAnnotation() {
        STNode atToken = parseAtToken();
        STNode annotReference;
        if (peek().kind != SyntaxKind.IDENTIFIER_TOKEN) {
            annotReference = STNodeFactory.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN);
        } else {
            annotReference = parseQualifiedIdentifier(ParserRuleContext.ANNOT_REFERENCE);
        }

        STNode annotValue;
        if (peek().kind == SyntaxKind.OPEN_BRACE_TOKEN) {
            annotValue = parseMappingConstructorExpr();
        } else {
            annotValue = STNodeFactory.createEmptyNode();
        }
        return STNodeFactory.createAnnotationNode(atToken, annotReference, annotValue);
    }

    /**
     * Parse '@' token.
     *
     * @return Parsed node
     */
    private STNode parseAtToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.AT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.AT);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse metadata. Meta data consist of optional doc string and
     * an annotations list.
     * <p>
     * <code>metadata := [DocumentationString] annots</code>
     *
     * @return Parse node
     */
    private STNode parseMetaData(SyntaxKind nextTokenKind) {
        STNode docString;
        STNode annotations;
        switch (nextTokenKind) {
            case DOCUMENTATION_LINE:
                
                docString = parseDocumentationString();
                annotations = parseAnnotations();
                break;
            case AT_TOKEN:
                docString = STNodeFactory.createEmptyNode();
                annotations = parseAnnotations(nextTokenKind);
                break;
            default:
                return createEmptyMetadata();
        }

        return STNodeFactory.createMetadataNode(docString, annotations);
    }

    /**
     * Create empty metadata node.
     *
     * @return A metadata node with no doc string and no annotations
     */
    private STNode createEmptyMetadata() {
        return STNodeFactory.createMetadataNode(STNodeFactory.createEmptyNode(),
                STNodeFactory.createNodeList(new ArrayList<>()));
    }

    /**
     * Parse is expression.
     * <code>
     * is-expr := expression is type-descriptor
     * </code>
     *
     * @param lhsExpr Preceding expression of the is expression
     * @return Is expression node
     */
    private STNode parseTypeTestExpression(STNode lhsExpr) {
        STNode isKeyword = parseIsKeyword();
        STNode typeDescriptor = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_EXPRESSION);
        return STNodeFactory.createTypeTestExpressionNode(lhsExpr, isKeyword, typeDescriptor);
    }

    /**
     * Parse is-keyword.
     *
     * @return Is-keyword node
     */
    private STNode parseIsKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.IS_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.IS_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse local type definition statement statement.
     * <code>ocal-type-defn-stmt := [annots] type identifier type-descriptor ;</code>
     *
     * @return local type definition statement statement
     */
    private STNode parseLocalTypeDefinitionStatement(STNode annots) {
        startContext(ParserRuleContext.LOCAL_TYPE_DEFINITION_STMT);
        STNode typeKeyword = parseTypeKeyword();
        STNode typeName = parseTypeName();
        STNode typeDescriptor = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TYPE_DEF);
        STNode semicolon = parseSemicolon();
        endContext();
        return STNodeFactory.createLocalTypeDefinitionStatementNode(annots, typeKeyword, typeName, typeDescriptor,
                semicolon);
    }

    /**
     * Pass statements that starts with an identifier.
     *
     * @param tokenKind Next token kind
     * @return Parsed node
     */
    private STNode parseStatementStartsWithIdentifier(STNode annots) {
        startContext(ParserRuleContext.AMBIGUOUS_STMT);
        STNode identifier = parseStatementStartIdentifier();
        STNode stmt = parseStatementStartIdentifierRhs(annots, identifier);
        endContext();
        return stmt;
    }

    private STNode parseStatementStartIdentifierRhs(STNode annots, STNode identifier) {
        return parseStatementStartIdentifierRhs(peek().kind, annots, identifier);
    }

    private STNode parseStatementStartIdentifierRhs(SyntaxKind nextTokenKind, STNode annots, STNode identifier) {
        switch (nextTokenKind) {
            case OPEN_BRACKET_TOKEN:
                STNode tbpOrMemberAccess =
                        parseTypedBindingPatternOrMemberAccess(identifier, false, ParserRuleContext.AMBIGUOUS_STMT);
                if (tbpOrMemberAccess.kind == SyntaxKind.INDEXED_EXPRESSION) {
                    STNode expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, tbpOrMemberAccess, false, false);
                    return parseStatementStartWithExpr(annots, expr);
                }

                STNode finalKeyword = STNodeFactory.createEmptyNode();
                return parseVarDeclRhs(annots, finalKeyword, tbpOrMemberAccess, false);
            case IDENTIFIER_TOKEN:
            case QUESTION_MARK_TOKEN:
                
                return parseTypeDescStartsWithIdentifier(identifier, annots);
            case EQUAL_TOKEN:
            case SEMICOLON_TOKEN:
                
                return parseStatementStartWithExpr(nextTokenKind, annots, identifier);
            case PIPE_TOKEN:
            case BITWISE_AND_TOKEN:
                STToken nextNextToken = peek(2);
                if (nextNextToken.kind != SyntaxKind.EQUAL_TOKEN) {
                    return parseTypeDescStartsWithIdentifier(identifier, annots);
                }
                
            default:
                
                if (isCompoundBinaryOperator(nextTokenKind)) {
                    return parseCompoundAssignmentStmtRhs(identifier);
                }
                
                
                if (isValidExprRhsStart(nextTokenKind)) {
                    STNode expression =
                            parseExpressionRhs(nextTokenKind, DEFAULT_OP_PRECEDENCE, identifier, false, true);
                    return parseStatementStartWithExpr(annots, expression);
                }

                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.STMT_START_IDENTIFIER_RHS, annots, identifier);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseStatementStartIdentifierRhs(solution.tokenKind, annots, identifier);
        }
    }

    private STNode parseTypeDescStartsWithIdentifier(STNode typeDesc, STNode annots) {
        switchContext(ParserRuleContext.VAR_DECL_STMT);

        
        
        startContext(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
        typeDesc = parseComplexTypeDescriptor(typeDesc, ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN, false);
        endContext();

        STNode finalKeyword = STNodeFactory.createEmptyNode();
        STNode typeBindingPattern = parseTypedBindingPatternTypeRhs(typeDesc, ParserRuleContext.VAR_DECL_STMT);
        return parseVarDeclRhs(annots, finalKeyword, typeBindingPattern, false);
    }

    /**
     * Parse statement which is only consists of an action or expression.
     *
     * @param annots Annotations
     * @param nextTokenKind Next token kind
     * @return Parsed node
     */
    private STNode parseExpressionStament(SyntaxKind nextTokenKind, STNode annots) {
        startContext(ParserRuleContext.EXPRESSION_STATEMENT);
        STNode expression = parseActionOrExpressionInLhs(nextTokenKind, annots);
        STNode stmt = getExpressionAsStatement(expression);
        endContext();
        return stmt;
    }

    private STNode parseStamentStartWithExpr(SyntaxKind nextTokenKind, STNode annots) {
        startContext(ParserRuleContext.EXPRESSION_STATEMENT);
        STNode expression = parseActionOrExpressionInLhs(nextTokenKind, annots);
        STNode stmt = parseStatementStartWithExpr(annots, expression);
        endContext();
        return stmt;
    }

    /**
     * Parse statements that starts with an expression.
     *
     * @param annots Annotations
     * @return Parsed node
     */
    private STNode parseStatementStartWithExpr(STNode annots, STNode expression) {
        STToken nextToken = peek();
        return parseStatementStartWithExpr(nextToken.kind, annots, expression);
    }

    /**
     * Parse the component followed by the expression, at the beginning of a statement.
     *
     * @param nextTokenKind Kind of the next token
     * @param annots Annotations
     * @return Parsed node
     */
    private STNode parseStatementStartWithExpr(SyntaxKind nextTokenKind, STNode annots, STNode expression) {
        switch (nextTokenKind) {
            case EQUAL_TOKEN:
                switchContext(ParserRuleContext.ASSIGNMENT_STMT);
                return parseAssignmentStmtRhs(expression);
            case SEMICOLON_TOKEN:
                return getExpressionAsStatement(expression);
            case IDENTIFIER_TOKEN:
            default:
                
                if (isCompoundBinaryOperator(nextTokenKind)) {
                    return parseCompoundAssignmentStmtRhs(expression);
                }

                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.STMT_START_WITH_EXPR_RHS, annots, expression);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseStatementStartWithExpr(solution.tokenKind, annots, expression);
        }
    }

    private STNode getArrayLength(STNodeList exprs) {
        if (exprs.isEmpty()) {
            return STNodeFactory.createEmptyNode();
        }

        STNode lengthExpr = exprs.get(0);
        switch (lengthExpr.kind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case ASTERISK_TOKEN:
            case SIMPLE_NAME_REFERENCE:
            case QUALIFIED_NAME_REFERENCE:
                break;
            default:
                this.errorHandler.reportInvalidNode(null, "invalid array length");
                break;
        }
        return lengthExpr;
    }

    private STNode getExpressionAsStatement(STNode expression) {
        switch (expression.kind) {
            case METHOD_CALL:
            case FUNCTION_CALL:
            case CHECK_EXPRESSION:
                return parseCallStatement(expression);
            case REMOTE_METHOD_CALL_ACTION:
            case CHECK_ACTION:
            case BRACED_ACTION:
            case START_ACTION:
            case TRAP_ACTION:
            case FLUSH_ACTION:
            case ASYNC_SEND_ACTION:
            case SYNC_SEND_ACTION:
            case RECEIVE_ACTION:
            case WAIT_ACTION:
            case QUERY_ACTION:
            case COMMIT_ACTION:
                return parseActionStatement(expression);
            default:
                
                
                this.errorHandler.reportInvalidNode(null,
                        "left hand side of an assignment must be a variable reference");

                STNode semicolon = parseSemicolon();
                return STNodeFactory.createExpressionStatementNode(SyntaxKind.INVALID, expression, semicolon);
        }
    }

    /**
     * <p>
     * Parse call statement, given the call expression.
     * <p>
     * <code>
     * call-stmt := call-expr ;
     * <br/>
     * call-expr := function-call-expr | method-call-expr | checking-keyword call-expr
     * </code>
     *
     * @param expression Call expression associated with the call statement
     * @return Call statement node
     */
    private STNode parseCallStatement(STNode expression) {
        validateExprInCallStmt(expression);
        STNode semicolon = parseSemicolon();
        return STNodeFactory.createExpressionStatementNode(SyntaxKind.CALL_STATEMENT, expression, semicolon);
    }

    private void validateExprInCallStmt(STNode expression) {
        switch (expression.kind) {
            case FUNCTION_CALL:
            case METHOD_CALL:
                break;
            case CHECK_EXPRESSION:
                validateExprInCallStmt(((STCheckExpressionNode) expression).expression);
                break;
            case REMOTE_METHOD_CALL_ACTION:
                break;
            case BRACED_EXPRESSION:
                validateExprInCallStmt(((STBracedExpressionNode) expression).expression);
                break;
            default:
                if (isMissingNode(expression)) {
                    break;
                }

                
                this.errorHandler.reportInvalidNode(null, "expression followed by the checking keyword must be a " +
                        "func-call, a method-call or a check-expr");
                break;
        }
    }

    /**
     * Check whether a node is a missing node.
     *
     * @param node Node to check
     * @return <code>true</code> if the node is a missing node. <code>false</code> otherwise
     */
    private boolean isMissingNode(STNode node) {
        if (node.kind == SyntaxKind.SIMPLE_NAME_REFERENCE) {
            return isMissingNode(((STSimpleNameReferenceNode) node).name);
        }
        return node instanceof STMissingToken;
    }

    private STNode parseActionStatement(STNode action) {
        STNode semicolon = parseSemicolon();
        return STNodeFactory.createExpressionStatementNode(SyntaxKind.ACTION_STATEMENT, action, semicolon);
    }

    /**
     * Parse remote method call action, given the starting expression.
     * <p>
     * <code>
     * remote-method-call-action := expression -> method-name ( arg-list )
     * <br/>
     * async-send-action := expression -> peer-worker ;
     * </code>
     *
     * @param isRhsExpr Is this an RHS action
     * @param expression LHS expression
     * @return
     */
    private STNode parseRemoteMethodCallOrAsyncSendAction(STNode expression, boolean isRhsExpr) {
        STNode rightArrow = parseRightArrow();
        return parseRemoteCallOrAsyncSendActionRhs(peek().kind, expression, isRhsExpr, rightArrow);
    }

    private STNode parseRemoteCallOrAsyncSendActionRhs(SyntaxKind nextTokenKind, STNode expression, boolean isRhsExpr,
                                                       STNode rightArrow) {
        STNode name;
        switch (nextTokenKind) {
            case DEFAULT_KEYWORD:
                name = parseDefaultKeyword();
                return parseAsyncSendAction(expression, rightArrow, name);
            case IDENTIFIER_TOKEN:
                name = STNodeFactory.createSimpleNameReferenceNode(parseFunctionName());
                break;
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.REMOTE_CALL_OR_ASYNC_SEND_RHS, expression,
                        isRhsExpr, rightArrow);

                
                
                
                if (solution.action == Action.REMOVE) {
                    name = solution.recoveredNode;
                    break;
                }

                return parseRemoteCallOrAsyncSendActionRhs(solution.tokenKind, expression, isRhsExpr, rightArrow);
        }

        return parseRemoteCallOrAsyncSendEnd(peek().kind, expression, rightArrow, name);
    }

    private STNode parseRemoteCallOrAsyncSendEnd(SyntaxKind nextTokenKind, STNode expression, STNode rightArrow,
                                                 STNode name) {
        switch (nextTokenKind) {
            case OPEN_PAREN_TOKEN:
                return parseRemoteMethodCallAction(expression, rightArrow, name);
            case SEMICOLON_TOKEN:
                return parseAsyncSendAction(expression, rightArrow, name);
            default:
                STToken token = peek();
                Solution solution =
                        recover(token, ParserRuleContext.REMOTE_CALL_OR_ASYNC_SEND_END, expression, rightArrow, name);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseRemoteCallOrAsyncSendEnd(solution.tokenKind, expression, rightArrow, name);
        }
    }

    /**
     * Parse default keyword.
     *
     * @return default keyword node
     */
    private STNode parseDefaultKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.DEFAULT_KEYWORD) {
            return STNodeFactory.createSimpleNameReferenceNode(consume());
        } else {
            Solution sol = recover(token, ParserRuleContext.DEFAULT_KEYWORD);
            return sol.recoveredNode;
        }
    }

    private STNode parseAsyncSendAction(STNode expression, STNode rightArrow, STNode peerWorker) {
        return STNodeFactory.createAsyncSendActionNode(expression, rightArrow, peerWorker);
    }

    private STNode parseRemoteMethodCallAction(STNode expression, STNode rightArrow, STNode name) {
        STNode openParenToken = parseOpenParenthesis(ParserRuleContext.ARG_LIST_START);
        STNode arguments = parseArgsList();
        STNode closeParenToken = parseCloseParenthesis();
        return STNodeFactory.createRemoteMethodCallActionNode(expression, rightArrow, name, openParenToken, arguments,
                closeParenToken);
    }

    /**
     * Parse right arrow (<code>-></code>) token.
     *
     * @return Parsed node
     */
    private STNode parseRightArrow() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.RIGHT_ARROW_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.RIGHT_ARROW);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse parameterized type descriptor.
     * parameterized-type-descriptor := map type-parameter | future type-parameter | typedesc type-parameter
     *
     * @return Parsed node
     */
    private STNode parseParameterizedTypeDescriptor() {
        STNode parameterizedTypeKeyword = parseParameterizedTypeKeyword();
        STNode ltToken = parseLTToken();
        STNode typeNode = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANGLE_BRACKETS);
        STNode gtToken = parseGTToken();
        return STNodeFactory.createParameterizedTypeDescriptorNode(parameterizedTypeKeyword, ltToken, typeNode,
                gtToken);
    }

    /**
     * Parse <code>map</code> or <code>future</code> or <code>typedesc</code> keyword token.
     *
     * @return Parsed node
     */
    private STNode parseParameterizedTypeKeyword() {
        STToken nextToken = peek();
        switch (nextToken.kind) {
            case MAP_KEYWORD: 
            case FUTURE_KEYWORD: 
            case TYPEDESC_KEYWORD: 
                return consume();
            default:
                Solution sol = recover(nextToken, ParserRuleContext.PARAMETERIZED_TYPE);
                return sol.recoveredNode;
        }
    }

    /**
     * Parse <code> < </code> token.
     *
     * @return Parsed node
     */
    private STNode parseGTToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.GT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.GT);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse <code> > </code> token.
     *
     * @return Parsed node
     */
    private STNode parseLTToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.LT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.LT);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse nil literal. Here nil literal is only referred to ( ).
     *
     * @return Parsed node
     */
    private STNode parseNilLiteral() {
        startContext(ParserRuleContext.NIL_LITERAL);
        STNode openParenthesisToken = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
        STNode closeParenthesisToken = parseCloseParenthesis();
        endContext();
        return STNodeFactory.createNilLiteralNode(openParenthesisToken, closeParenthesisToken);
    }

    /**
     * Parse annotation declaration, given the qualifier.
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the listener declaration
     * @param constKeyword Const keyword
     * @return Parsed node
     */
    private STNode parseAnnotationDeclaration(STNode metadata, STNode qualifier, STNode constKeyword) {
        startContext(ParserRuleContext.ANNOTATION_DECL);
        STNode annotationKeyword = parseAnnotationKeyword();
        STNode annotDecl = parseAnnotationDeclFromType(metadata, qualifier, constKeyword, annotationKeyword);
        endContext();
        return annotDecl;
    }

    /**
     * Parse annotation keyword.
     *
     * @return Parsed node
     */
    private STNode parseAnnotationKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ANNOTATION_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ANNOTATION_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse the components that follows after the annotation keyword of a annotation declaration.
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the constant decl
     * @param constKeyword Const keyword
     * @param annotationKeyword
     * @return Parsed node
     */
    private STNode parseAnnotationDeclFromType(STNode metadata, STNode qualifier, STNode constKeyword,
                                               STNode annotationKeyword) {
        STToken nextToken = peek();
        return parseAnnotationDeclFromType(nextToken.kind, metadata, qualifier, constKeyword, annotationKeyword);
    }

    private STNode parseAnnotationDeclFromType(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
                                               STNode constKeyword, STNode annotationKeyword) {
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN:
                return parseAnnotationDeclWithOptionalType(metadata, qualifier, constKeyword, annotationKeyword);
            default:
                if (isTypeStartingToken(nextTokenKind)) {
                    break;
                }

                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.ANNOT_DECL_OPTIONAL_TYPE, metadata, qualifier,
                        constKeyword, annotationKeyword);
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseAnnotationDeclFromType(solution.tokenKind, metadata, qualifier, constKeyword,
                        annotationKeyword);
        }

        STNode typeDesc = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANNOTATION_DECL);
        STNode annotTag = parseAnnotationTag();
        return parseAnnotationDeclAttachPoints(metadata, qualifier, constKeyword, annotationKeyword, typeDesc,
                annotTag);
    }

    /**
     * Parse annotation tag.
     * <p>
     * <code>annot-tag := identifier</code>
     *
     * @return
     */
    private STNode parseAnnotationTag() {
        STToken token = peek();
        if (token.kind == SyntaxKind.IDENTIFIER_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(peek(), ParserRuleContext.ANNOTATION_TAG);
            return sol.recoveredNode;
        }
    }

    private STNode parseAnnotationDeclWithOptionalType(STNode metadata, STNode qualifier, STNode constKeyword,
                                                       STNode annotationKeyword) {
        
        
        STNode typeDescOrAnnotTag = parseAnnotationTag();
        if (typeDescOrAnnotTag.kind == SyntaxKind.QUALIFIED_NAME_REFERENCE) {
            STNode annotTag = parseAnnotationTag();
            return parseAnnotationDeclAttachPoints(metadata, qualifier, constKeyword, annotationKeyword,
                    typeDescOrAnnotTag, annotTag);
        }

        return parseAnnotationDeclRhs(metadata, qualifier, constKeyword, annotationKeyword, typeDescOrAnnotTag);
    }

    /**
     * Parse the component that follows the first identifier in an annotation decl. The identifier
     * can be either the type-name (a user defined type) or the annot-tag, where the type-name
     * is not present.
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the annotation decl
     * @param constKeyword Const keyword
     * @param annotationKeyword Annotation keyword
     * @param typeDescOrAnnotTag Identifier that follows the annotation-keyword
     * @return Parsed node
     */
    private STNode parseAnnotationDeclRhs(STNode metadata, STNode qualifier, STNode constKeyword,
                                          STNode annotationKeyword, STNode typeDescOrAnnotTag) {
        STToken token = peek();
        return parseAnnotationDeclRhs(token.kind, metadata, qualifier, constKeyword, annotationKeyword,
                typeDescOrAnnotTag);
    }

    private STNode parseAnnotationDeclRhs(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
                                          STNode constKeyword, STNode annotationKeyword, STNode typeDescOrAnnotTag) {
        STNode typeDesc;
        STNode annotTag;
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN:
                typeDesc = typeDescOrAnnotTag;
                annotTag = parseAnnotationTag();
                break;
            case SEMICOLON_TOKEN:
            case ON_KEYWORD:
                typeDesc = STNodeFactory.createEmptyNode();
                annotTag = typeDescOrAnnotTag;
                break;
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.ANNOT_DECL_RHS, metadata, qualifier, constKeyword,
                        annotationKeyword, typeDescOrAnnotTag);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseAnnotationDeclRhs(solution.tokenKind, metadata, qualifier, constKeyword, annotationKeyword,
                        typeDescOrAnnotTag);
        }

        return parseAnnotationDeclAttachPoints(metadata, qualifier, constKeyword, annotationKeyword, typeDesc,
                annotTag);
    }

    private STNode parseAnnotationDeclAttachPoints(STNode metadata, STNode qualifier, STNode constKeyword,
                                                   STNode annotationKeyword, STNode typeDesc, STNode annotTag) {
        STToken nextToken = peek();
        return parseAnnotationDeclAttachPoints(nextToken.kind, metadata, qualifier, constKeyword, annotationKeyword,
                typeDesc, annotTag);

    }

    private STNode parseAnnotationDeclAttachPoints(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
                                                   STNode constKeyword, STNode annotationKeyword, STNode typeDesc,
                                                   STNode annotTag) {
        STNode onKeyword;
        STNode attachPoints;
        switch (nextTokenKind) {
            case SEMICOLON_TOKEN:
                onKeyword = STNodeFactory.createEmptyNode();
                attachPoints = STNodeFactory.createEmptyNode();
                break;
            case ON_KEYWORD:
                onKeyword = parseOnKeyword();
                attachPoints = parseAnnotationAttachPoints();
                break;
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.ANNOT_OPTIONAL_ATTACH_POINTS, metadata, qualifier,
                        constKeyword, annotationKeyword, typeDesc, annotTag);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseAnnotationDeclAttachPoints(solution.tokenKind, metadata, qualifier, constKeyword,
                        annotationKeyword, typeDesc, annotTag);
        }

        STNode semicolonToken = parseSemicolon();
        return STNodeFactory.createAnnotationDeclarationNode(metadata, qualifier, constKeyword, annotationKeyword,
                typeDesc, annotTag, onKeyword, attachPoints, semicolonToken);
    }

    /**
     * Parse annotation attach points.
     * <p>
     * <code>
     * annot-attach-points := annot-attach-point (, annot-attach-point)*
     * <br/><br/>
     * annot-attach-point := dual-attach-point | source-only-attach-point
     * <br/><br/>
     * dual-attach-point := [source] dual-attach-point-ident
     * <br/><br/>
     * dual-attach-point-ident :=
     *     [object] type
     *     | [object|resource] function
     *     | parameter
     *     | return
     *     | service
     *     | [object|record] field
     * <br/><br/>
     * source-only-attach-point := source source-only-attach-point-ident
     * <br/><br/>
     * source-only-attach-point-ident :=
     *     annotation
     *     | external
     *     | var
     *     | const
     *     | listener
     *     | worker
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseAnnotationAttachPoints() {
        startContext(ParserRuleContext.ANNOT_ATTACH_POINTS_LIST);
        List<STNode> attachPoints = new ArrayList<>();

        STToken nextToken = peek();
        if (isEndAnnotAttachPointList(nextToken.kind)) {
            endContext();
            this.errorHandler.reportMissingTokenError("missing attach point");
            return STNodeFactory.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN);
        }

        
        STNode attachPoint = parseAnnotationAttachPoint();
        attachPoints.add(attachPoint);

        
        nextToken = peek();
        STNode leadingComma;
        while (!isEndAnnotAttachPointList(nextToken.kind)) {
            leadingComma = parseAttachPointEnd();
            if (leadingComma == null) {
                break;
            }
            attachPoints.add(leadingComma);

            
            attachPoint = parseAnnotationAttachPoint();
            if (attachPoint == null) {
                this.errorHandler.reportMissingTokenError("missing attach point");
                attachPoint = STNodeFactory.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN);
                attachPoints.add(attachPoint);
                break;
            }

            attachPoints.add(attachPoint);
            nextToken = peek();
        }

        endContext();
        return STNodeFactory.createNodeList(attachPoints);
    }

    /**
     * Parse annotation attach point end.
     *
     * @return Parsed node
     */
    private STNode parseAttachPointEnd() {
        STToken nextToken = peek();
        return parseAttachPointEnd(nextToken.kind);
    }

    private STNode parseAttachPointEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case SEMICOLON_TOKEN:
                
                return null;
            case COMMA_TOKEN:
                return consume();
            default:
                Solution sol = recover(peek(), ParserRuleContext.ATTACH_POINT_END);
                if (sol.action == Action.REMOVE) {
                    return sol.recoveredNode;
                }

                return sol.tokenKind == SyntaxKind.COMMA_TOKEN ? sol.recoveredNode : null;
        }
    }

    private boolean isEndAnnotAttachPointList(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case EOF_TOKEN:
            case SEMICOLON_TOKEN:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse annotation attach point.
     *
     * @return Parsed node
     */
    private STNode parseAnnotationAttachPoint() {
        return parseAnnotationAttachPoint(peek().kind);
    }

    private STNode parseAnnotationAttachPoint(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case EOF_TOKEN:
                return null;

            
            case ANNOTATION_KEYWORD:
            case EXTERNAL_KEYWORD:
            case VAR_KEYWORD:
            case CONST_KEYWORD:
            case LISTENER_KEYWORD:
            case WORKER_KEYWORD:
                

            case SOURCE_KEYWORD:
                STNode sourceKeyword = parseSourceKeyword();
                return parseAttachPointIdent(sourceKeyword);

            
            case OBJECT_KEYWORD:
            case TYPE_KEYWORD:
            case RESOURCE_KEYWORD:
            case FUNCTION_KEYWORD:
            case PARAMETER_KEYWORD:
            case RETURN_KEYWORD:
            case SERVICE_KEYWORD:
            case FIELD_KEYWORD:
            case RECORD_KEYWORD:
                sourceKeyword = STNodeFactory.createEmptyNode();
                STNode firstIdent = consume();
                return parseDualAttachPointIdent(sourceKeyword, firstIdent);
            default:
                Solution solution = recover(peek(), ParserRuleContext.ATTACH_POINT);
                return solution.recoveredNode;
        }
    }

    /**
     * Parse source keyword.
     *
     * @return Parsed node
     */
    private STNode parseSourceKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.SOURCE_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.SOURCE_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse attach point ident gievn.
     * <p>
     * <code>
     * source-only-attach-point-ident := annotation | external | var | const | listener | worker
     * <br/><br/>
     * dual-attach-point-ident := [object] type | [object|resource] function | parameter
     *                            | return | service | [object|record] field
     * </code>
     *
     * @param sourceKeyword Source keyword
     * @return Parsed node
     */
    private STNode parseAttachPointIdent(STNode sourceKeyword) {
        return parseAttachPointIdent(peek().kind, sourceKeyword);
    }

    private STNode parseAttachPointIdent(SyntaxKind nextTokenKind, STNode sourceKeyword) {
        switch (nextTokenKind) {
            case ANNOTATION_KEYWORD:
            case EXTERNAL_KEYWORD:
            case VAR_KEYWORD:
            case CONST_KEYWORD:
            case LISTENER_KEYWORD:
            case WORKER_KEYWORD:
                STNode firstIdent = consume();
                STNode secondIdent = STNodeFactory.createEmptyNode();
                return STNodeFactory.createAnnotationAttachPointNode(sourceKeyword, firstIdent, secondIdent);
            case OBJECT_KEYWORD:
            case RESOURCE_KEYWORD:
            case RECORD_KEYWORD:
            case TYPE_KEYWORD:
            case FUNCTION_KEYWORD:
            case PARAMETER_KEYWORD:
            case RETURN_KEYWORD:
            case SERVICE_KEYWORD:
            case FIELD_KEYWORD:
                firstIdent = consume();
                return parseDualAttachPointIdent(sourceKeyword, firstIdent);
            default:
                Solution solution = recover(peek(), ParserRuleContext.ATTACH_POINT_IDENT, sourceKeyword);
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                firstIdent = solution.recoveredNode;
                return parseDualAttachPointIdent(sourceKeyword, firstIdent);
        }
    }

    /**
     * Parse dual-attach-point ident.
     *
     * @param sourceKeyword Source keyword
     * @param firstIdent first part of the dual attach-point
     * @return Parsed node
     */
    private STNode parseDualAttachPointIdent(STNode sourceKeyword, STNode firstIdent) {
        STNode secondIdent;
        switch (firstIdent.kind) {
            case OBJECT_KEYWORD:
                secondIdent = parseIdentAfterObjectIdent();
                break;
            case RESOURCE_KEYWORD:
                secondIdent = parseFunctionIdent();
                break;
            case RECORD_KEYWORD:
                secondIdent = parseFieldIdent();
                break;
            case TYPE_KEYWORD:
            case FUNCTION_KEYWORD:
            case PARAMETER_KEYWORD:
            case RETURN_KEYWORD:
            case SERVICE_KEYWORD:
            case FIELD_KEYWORD:
            default: 
                secondIdent = STNodeFactory.createEmptyNode();
                break;
        }

        return STNodeFactory.createAnnotationAttachPointNode(sourceKeyword, firstIdent, secondIdent);
    }

    /**
     * Parse the idents that are supported after object-ident.
     *
     * @return Parsed node
     */
    private STNode parseIdentAfterObjectIdent() {
        STToken token = peek();
        switch (token.kind) {
            case TYPE_KEYWORD:
            case FUNCTION_KEYWORD:
            case FIELD_KEYWORD:
                return consume();
            default:
                Solution sol = recover(token, ParserRuleContext.IDENT_AFTER_OBJECT_IDENT);
                return sol.recoveredNode;
        }
    }

    /**
     * Parse function ident.
     *
     * @return Parsed node
     */
    private STNode parseFunctionIdent() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FUNCTION_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FUNCTION_IDENT);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse field ident.
     *
     * @return Parsed node
     */
    private STNode parseFieldIdent() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FIELD_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FIELD_IDENT);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse XML namespace declaration.
     * <p>
     * <code>xmlns-decl := xmlns xml-namespace-uri [ as xml-namespace-prefix ] ;
     * <br/>
     * xml-namespace-uri := simple-const-expr
     * <br/>
     * xml-namespace-prefix := identifier
     * </code>
     *
     * @return
     */
    private STNode parseXMLNamepsaceDeclaration() {
        startContext(ParserRuleContext.XML_NAMESPACE_DECLARATION);
        STNode xmlnsKeyword = parseXMLNSKeyword();
        STNode namespaceUri = parseXMLNamespaceUri();
        STNode xmlnsDecl = parseXMLDeclRhs(xmlnsKeyword, namespaceUri);
        endContext();
        return xmlnsDecl;
    }

    /**
     * Parse xmlns keyword.
     *
     * @return Parsed node
     */
    private STNode parseXMLNSKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.XMLNS_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.XMLNS_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse namespace uri.
     *
     * @return Parsed node
     */
    private STNode parseXMLNamespaceUri() {
        STNode expr = parseConstExpr();
        switch (expr.kind) {
            case STRING_LITERAL:
            case IDENTIFIER_TOKEN:
            case QUALIFIED_NAME_REFERENCE:
                break;
            default:
                this.errorHandler.reportInvalidNode(null, "namespace uri must be a subtype of string");
        }

        return expr;
    }

    private STNode parseConstExpr() {
        startContext(ParserRuleContext.CONSTANT_EXPRESSION);
        STNode expr = parseConstExprInternal();
        endContext();
        return expr;
    }

    private STNode parseConstExprInternal() {
        STToken nextToken = peek();
        return parseConstExprInternal(nextToken.kind);
    }

    /**
     * Parse constants expr.
     *
     * @return Parsed node
     */
    private STNode parseConstExprInternal(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case STRING_LITERAL:
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case DECIMAL_FLOATING_POINT_LITERAL:
            case HEX_FLOATING_POINT_LITERAL:
            case TRUE_KEYWORD:
            case FALSE_KEYWORD:
            case NULL_KEYWORD:
                return parseBasicLiteral();
            case IDENTIFIER_TOKEN:
                return parseQualifiedIdentifier(ParserRuleContext.VARIABLE_REF);
            case PLUS_TOKEN:
            case MINUS_TOKEN:
                return parseSignedIntOrFloat();
            case OPEN_BRACE_TOKEN:
                return parseNilLiteral();
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.CONSTANT_EXPRESSION_START);
                return solution.recoveredNode;
        }
    }

    /**
     * Parse the portion after the namsepsace-uri of an XML declaration.
     *
     * @param xmlnsKeyword XMLNS keyword
     * @param namespaceUri Namespace URI
     * @return Parsed node
     */
    private STNode parseXMLDeclRhs(STNode xmlnsKeyword, STNode namespaceUri) {
        return parseXMLDeclRhs(peek().kind, xmlnsKeyword, namespaceUri);
    }

    private STNode parseXMLDeclRhs(SyntaxKind nextTokenKind, STNode xmlnsKeyword, STNode namespaceUri) {
        STNode asKeyword = STNodeFactory.createEmptyNode();
        STNode namespacePrefix = STNodeFactory.createEmptyNode();

        switch (nextTokenKind) {
            case AS_KEYWORD:
                asKeyword = parseAsKeyword();
                namespacePrefix = parseNamespacePrefix();
                break;
            case SEMICOLON_TOKEN:
                break;
            default:
                STToken token = peek();
                Solution solution =
                        recover(token, ParserRuleContext.XML_NAMESPACE_PREFIX_DECL, xmlnsKeyword, namespaceUri);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseXMLDeclRhs(solution.tokenKind, xmlnsKeyword, namespaceUri);
        }
        STNode semicolon = parseSemicolon();
        return STNodeFactory.createXMLNamespaceDeclarationNode(xmlnsKeyword, namespaceUri, asKeyword, namespacePrefix,
                semicolon);
    }

    /**
     * Parse import prefix.
     *
     * @return Parsed node
     */
    private STNode parseNamespacePrefix() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.IDENTIFIER_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(peek(), ParserRuleContext.NAMESPACE_PREFIX);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse named worker declaration.
     * <p>
     * <code>named-worker-decl := [annots] worker worker-name return-type-descriptor { sequence-stmt }</code>
     *
     * @param annots Annotations attached to the worker decl
     * @return Parsed node
     */
    private STNode parseNamedWorkerDeclaration(STNode annots) {
        startContext(ParserRuleContext.NAMED_WORKER_DECL);
        STNode workerKeyword = parseWorkerKeyword();
        STNode workerName = parseWorkerName();
        STNode returnTypeDesc = parseReturnTypeDescriptor();
        STNode workerBody = parseBlockNode();
        endContext();
        return STNodeFactory.createNamedWorkerDeclarationNode(annots, workerKeyword, workerName, returnTypeDesc,
                workerBody);
    }

    private STNode parseReturnTypeDescriptor() {
        
        STToken token = peek();
        if (token.kind != SyntaxKind.RETURNS_KEYWORD) {
            return STNodeFactory.createEmptyNode();
        }

        STNode returnsKeyword = consume();
        STNode annot = parseAnnotations();
        STNode type = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_RETURN_TYPE_DESC);
        return STNodeFactory.createReturnTypeDescriptorNode(returnsKeyword, annot, type);
    }

    /**
     * Parse worker keyword.
     *
     * @return Parsed node
     */
    private STNode parseWorkerKeyword() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.WORKER_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(peek(), ParserRuleContext.WORKER_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse worker name.
     * <p>
     * <code>worker-name := identifier</code>
     *
     * @return Parsed node
     */
    private STNode parseWorkerName() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.IDENTIFIER_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(peek(), ParserRuleContext.WORKER_NAME);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse documentation string.
     * <p>
     * <code>DocumentationString := DocumentationLine +</code>
     * <p>
     * Refer {@link BallerinaLexer
     *
     * @return Parsed node
     */
    private STNode parseDocumentationString() {
        List<STNode> docLines = new ArrayList<>();
        STToken nextToken = peek();
        while (nextToken.kind == SyntaxKind.DOCUMENTATION_LINE) {
            docLines.add(consume());
            nextToken = peek();
        }

        STNode documentationLines = STNodeFactory.createNodeList(docLines);
        return STNodeFactory.createDocumentationStringNode(documentationLines);
    }

    /**
     * Parse lock statement.
     * <code>lock-stmt := lock block-stmt ;</code>
     *
     * @return Lock statement
     */
    private STNode parseLockStatement() {
        startContext(ParserRuleContext.LOCK_STMT);
        STNode lockKeyword = parseLockKeyword();
        STNode blockStatement = parseBlockNode();
        endContext();
        return STNodeFactory.createLockStatementNode(lockKeyword, blockStatement);
    }

    /**
     * Parse lock-keyword.
     *
     * @return lock-keyword node
     */
    private STNode parseLockKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.LOCK_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.LOCK_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse union type descriptor.
     * union-type-descriptor := type-descriptor | type-descriptor
     *
     * @param leftTypeDesc Type desc in the LHS os the union type desc.
     * @param context Current context.
     * @return parsed union type desc node
     */
    private STNode parseUnionTypeDescriptor(STNode leftTypeDesc, ParserRuleContext context) {
        STNode pipeToken = parsePipeToken();
        STNode rightTypeDesc = parseTypeDescriptor(context);

        return STNodeFactory.createUnionTypeDescriptorNode(leftTypeDesc, pipeToken, rightTypeDesc);
    }

    /**
     * Parse pipe token.
     *
     * @return parsed pipe token node
     */
    private STNode parsePipeToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.PIPE_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.PIPE);
            return sol.recoveredNode;
        }
    }

    private boolean isTypeStartingToken(SyntaxKind nodeKind) {
        switch (nodeKind) {
            case IDENTIFIER_TOKEN:
            case SERVICE_KEYWORD:
            case RECORD_KEYWORD:
            case OBJECT_KEYWORD:
            case ABSTRACT_KEYWORD:
            case CLIENT_KEYWORD:
            case OPEN_PAREN_TOKEN: 
            case MAP_KEYWORD: 
            case FUTURE_KEYWORD: 
            case TYPEDESC_KEYWORD: 
            case ERROR_KEYWORD: 
            case STREAM_KEYWORD: 
            case TABLE_KEYWORD: 
            case FUNCTION_KEYWORD:
            case OPEN_BRACKET_TOKEN:
                return true;
            default:
                if (isSingletonTypeDescStart(nodeKind, true)) {
                    return true;
                }
                return isSimpleType(nodeKind);
        }
    }

    static boolean isSimpleType(SyntaxKind nodeKind) {
        switch (nodeKind) {
            case INT_KEYWORD:
            case FLOAT_KEYWORD:
            case DECIMAL_KEYWORD:
            case BOOLEAN_KEYWORD:
            case STRING_KEYWORD:
            case BYTE_KEYWORD:
            case XML_KEYWORD:
            case JSON_KEYWORD:
            case HANDLE_KEYWORD:
            case ANY_KEYWORD:
            case ANYDATA_KEYWORD:
            case NEVER_KEYWORD:
            case SERVICE_KEYWORD:
            case VAR_KEYWORD:
            case ERROR_KEYWORD: 
            case STREAM_KEYWORD: 
            case READONLY_KEYWORD:
            case DISTINCT_KEYWORD:
                return true;
            case TYPE_DESC:
                
                
                
                return true;
            default:
                return false;
        }
    }

    private SyntaxKind getTypeSyntaxKind(SyntaxKind typeKeyword) {
        switch (typeKeyword) {
            case INT_KEYWORD:
                return SyntaxKind.INT_TYPE_DESC;
            case FLOAT_KEYWORD:
                return SyntaxKind.FLOAT_TYPE_DESC;
            case DECIMAL_KEYWORD:
                return SyntaxKind.DECIMAL_TYPE_DESC;
            case BOOLEAN_KEYWORD:
                return SyntaxKind.BOOLEAN_TYPE_DESC;
            case STRING_KEYWORD:
                return SyntaxKind.STRING_TYPE_DESC;
            case BYTE_KEYWORD:
                return SyntaxKind.BYTE_TYPE_DESC;
            case XML_KEYWORD:
                return SyntaxKind.XML_TYPE_DESC;
            case JSON_KEYWORD:
                return SyntaxKind.JSON_TYPE_DESC;
            case HANDLE_KEYWORD:
                return SyntaxKind.HANDLE_TYPE_DESC;
            case ANY_KEYWORD:
                return SyntaxKind.ANY_TYPE_DESC;
            case ANYDATA_KEYWORD:
                return SyntaxKind.ANYDATA_TYPE_DESC;
            case NEVER_KEYWORD:
                return SyntaxKind.NEVER_TYPE_DESC;
            case SERVICE_KEYWORD:
                return SyntaxKind.SERVICE_TYPE_DESC;
            case VAR_KEYWORD:
                return SyntaxKind.VAR_TYPE_DESC;
            default:
                return SyntaxKind.TYPE_DESC;
        }
    }

    /**
     * Parse fork-keyword.
     *
     * @return Fork-keyword node
     */
    private STNode parseForkKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FORK_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FORK_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse multiple named worker declarations.
     *
     * @return named-worker-declarations node array
     */
    private STNode parseMultipleNamedWorkerDeclarations() {
        ArrayList<STNode> workers = new ArrayList<>();
        while (!isEndOfStatements()) {
            STNode stmt = parseStatement();
            if (stmt == null) {
                break;
            }

            switch (stmt.kind) {
                case NAMED_WORKER_DECLARATION:
                    workers.add(stmt);
                    break;
                default:
                    this.errorHandler.reportInvalidNode(null, "Only named-workers are allowed here");
                    break;
            }
        }

        if (workers.isEmpty()) {
            this.errorHandler.reportInvalidNode(null, "Fork Statement must contain atleast one named-worker");
        }
        STNode namedWorkers = STNodeFactory.createNodeList(workers);
        return namedWorkers;
    }

    /**
     * Parse fork statement.
     * <code>fork-stmt := fork { named-worker-decl+ }</code>
     *
     * @return Fork statement
     */
    private STNode parseForkStatement() {
        startContext(ParserRuleContext.FORK_STMT);
        STNode forkKeyword = parseForkKeyword();
        STNode openBrace = parseOpenBrace();
        STNode namedWorkerDeclarations = parseMultipleNamedWorkerDeclarations();
        STNode closeBrace = parseCloseBrace();
        endContext();
        return STNodeFactory.createForkStatementNode(forkKeyword, openBrace, namedWorkerDeclarations, closeBrace);
    }

    /**
     * Parse decimal floating point literal.
     *
     * @return Parsed node
     */
    private STNode parseDecimalFloatingPointLiteral() {
        STToken token = peek();
        if (token.kind == SyntaxKind.DECIMAL_FLOATING_POINT_LITERAL) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.DECIMAL_FLOATING_POINT_LITERAL);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse hex floating point literal.
     *
     * @return Parsed node
     */
    private STNode parseHexFloatingPointLiteral() {
        STToken token = peek();
        if (token.kind == SyntaxKind.HEX_FLOATING_POINT_LITERAL) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.HEX_FLOATING_POINT_LITERAL);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse trap expression.
     * <p>
     * <code>
     * trap-expr := trap expression
     * </code>
     *
     * @param allowActions Allow actions
     * @param isRhsExpr Whether this is a RHS expression or not
     * @return Trap expression node
     */
    private STNode parseTrapExpression(boolean isRhsExpr, boolean allowActions) {
        STNode trapKeyword = parseTrapKeyword();
        STNode expr = parseExpression(OperatorPrecedence.EXPRESSION_ACTION, isRhsExpr, allowActions);
        if (isAction(expr)) {
            return STNodeFactory.createTrapExpressionNode(SyntaxKind.TRAP_ACTION, trapKeyword, expr);
        }

        return STNodeFactory.createTrapExpressionNode(SyntaxKind.TRAP_EXPRESSION, trapKeyword, expr);
    }

    /**
     * Parse trap-keyword.
     *
     * @return Trap-keyword node
     */
    private STNode parseTrapKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.TRAP_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.TRAP_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse list constructor expression.
     * <p>
     * <code>
     * list-constructor-expr := [ [ expr-list ] ]
     * <br/>
     * expr-list := expression (, expression)*
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseListConstructorExpr() {
        startContext(ParserRuleContext.LIST_CONSTRUCTOR);
        STNode openBracket = parseOpenBracket();
        STNode expressions = parseOptionalExpressionsList();
        STNode closeBracket = parseCloseBracket();
        endContext();
        return STNodeFactory.createListConstructorExpressionNode(openBracket, expressions, closeBracket);
    }

    /**
     * Parse optional expression list.
     *
     * @return Parsed node
     */
    private STNode parseOptionalExpressionsList() {
        List<STNode> expressions = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfListConstructor(nextToken.kind)) {
            return STNodeFactory.createNodeList(new ArrayList<>());
        }

        
        STNode expr = parseExpression();
        expressions.add(expr);

        
        nextToken = peek();
        STNode listConstructorMemberEnd;
        while (!isEndOfListConstructor(nextToken.kind)) {
            listConstructorMemberEnd = parseListConstructorMemberEnd(nextToken.kind);
            if (listConstructorMemberEnd == null) {
                break;
            }

            expressions.add(listConstructorMemberEnd);
            expr = parseExpression();
            expressions.add(expr);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(expressions);
    }

    private boolean isEndOfListConstructor(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACKET_TOKEN:
                return true;
            default:
                return false;
        }
    }

    private STNode parseListConstructorMemberEnd() {
        return parseListConstructorMemberEnd(peek().kind);
    }

    private STNode parseListConstructorMemberEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACKET_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.LIST_CONSTRUCTOR_MEMBER_END);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }
                return parseListConstructorMemberEnd(solution.tokenKind);
        }
    }

    /**
     * Parse foreach statement.
     * <code>foreach-stmt := foreach typed-binding-pattern in action-or-expr block-stmt</code>
     *
     * @return foreach statement
     */
    private STNode parseForEachStatement() {
        startContext(ParserRuleContext.FOREACH_STMT);
        STNode forEachKeyword = parseForEachKeyword();
        STNode typedBindingPattern = parseTypedBindingPattern(ParserRuleContext.FOREACH_STMT);
        STNode inKeyword = parseInKeyword();
        STNode actionOrExpr = parseActionOrExpression();
        STNode blockStatement = parseBlockNode();
        endContext();
        return STNodeFactory.createForEachStatementNode(forEachKeyword, typedBindingPattern, inKeyword, actionOrExpr,
                blockStatement);
    }

    /**
     * Parse foreach-keyword.
     *
     * @return ForEach-keyword node
     */
    private STNode parseForEachKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FOREACH_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FOREACH_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse in-keyword.
     *
     * @return In-keyword node
     */
    private STNode parseInKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.IN_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.IN_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse type cast expression.
     * <p>
     * <code>
     * type-cast-expr := < type-cast-param > expression
     * <br/>
     * type-cast-param := [annots] type-descriptor | annots
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseTypeCastExpr(boolean isRhsExpr) {
        startContext(ParserRuleContext.TYPE_CAST);
        STNode ltToken = parseLTToken();
        STNode typeCastParam = parseTypeCastParam();
        STNode gtToken = parseGTToken();
        endContext();

        
        
        STNode expression = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false);
        return STNodeFactory.createTypeCastExpressionNode(ltToken, typeCastParam, gtToken, expression);
    }

    private STNode parseTypeCastParam() {
        STNode annot;
        STNode type;
        STToken token = peek();

        switch (token.kind) {
            case AT_TOKEN:
                annot = parseAnnotations();
                token = peek();
                if (isTypeStartingToken(token.kind)) {
                    type = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANGLE_BRACKETS);
                } else {
                    type = STNodeFactory.createEmptyNode();
                }
                break;
            default:
                annot = STNodeFactory.createEmptyNode();
                type = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANGLE_BRACKETS);
                break;
        }

        return STNodeFactory.createTypeCastParamNode(getAnnotations(annot), type);
    }

    /**
     * Parse table constructor expression.
     * <p>
     * <code>
     * table-constructor-expr-rhs := [ [row-list] ]
     * </code>
     *
     * @param tableKeyword tableKeyword that precedes this rhs
     * @param keySpecifier keySpecifier that precedes this rhs
     * @return Parsed node
     */
    private STNode parseTableConstructorExprRhs(STNode tableKeyword, STNode keySpecifier) {
        switchContext(ParserRuleContext.TABLE_CONSTRUCTOR);
        STNode openBracket = parseOpenBracket();
        STNode rowList = parseRowList();
        STNode closeBracket = parseCloseBracket();
        return STNodeFactory.createTableConstructorExpressionNode(tableKeyword, keySpecifier, openBracket, rowList,
                closeBracket);
    }

    /**
     * Parse table-keyword.
     *
     * @return Table-keyword node
     */
    private STNode parseTableKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.TABLE_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.TABLE_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse table rows.
     * <p>
     * <code>row-list := [ mapping-constructor-expr (, mapping-constructor-expr)* ]</code>
     *
     * @return Parsed node
     */
    private STNode parseRowList() {
        List<STNode> mappings = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfTableRowList(nextToken.kind)) {
            return STNodeFactory.createNodeList(new ArrayList<>());
        }

        
        STNode mapExpr = parseMappingConstructorExpr();
        mappings.add(mapExpr);

        
        nextToken = peek();
        STNode leadingComma;
        while (!isEndOfTableRowList(nextToken.kind)) {
            leadingComma = parseComma();
            mappings.add(leadingComma);
            mapExpr = parseMappingConstructorExpr();
            mappings.add(mapExpr);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(mappings);
    }

    private boolean isEndOfTableRowList(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACKET_TOKEN:
                return true;
            case COMMA_TOKEN:
            case OPEN_BRACE_TOKEN:
                return false;
            default:
                return isEndOfMappingConstructor(tokenKind);
        }
    }

    /**
     * Parse key specifier.
     * <p>
     * <code>key-specifier := key ( [ field-name (, field-name)* ] )</code>
     *
     * @return Parsed node
     */
    private STNode parseKeySpecifier() {
        startContext(ParserRuleContext.KEY_SPECIFIER);
        STNode keyKeyword = parseKeyKeyword();
        STNode openParen = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
        STNode fieldNames = parseFieldNames();
        STNode closeParen = parseCloseParenthesis();
        endContext();
        return STNodeFactory.createKeySpecifierNode(keyKeyword, openParen, fieldNames, closeParen);
    }

    /**
     * Parse key-keyword.
     *
     * @return Key-keyword node
     */
    private STNode parseKeyKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.KEY_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.KEY_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse field names.
     * <p>
     * <code>field-name-list := [ field-name (, field-name)* ]</code>
     *
     * @return Parsed node
     */
    private STNode parseFieldNames() {
        List<STNode> fieldNames = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfFieldNamesList(nextToken.kind)) {
            return STNodeFactory.createNodeList(new ArrayList<>());
        }

        
        STNode fieldName = parseVariableName();
        fieldNames.add(fieldName);

        
        nextToken = peek();
        STNode leadingComma;
        while (!isEndOfFieldNamesList(nextToken.kind)) {
            leadingComma = parseComma();
            fieldNames.add(leadingComma);
            fieldName = parseVariableName();
            fieldNames.add(fieldName);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(fieldNames);
    }

    private boolean isEndOfFieldNamesList(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case COMMA_TOKEN:
            case IDENTIFIER_TOKEN:
                return false;
            default:
                return true;
        }
    }

    /**
     * Parse error type descriptor.
     * <p>
     * error-type-descriptor := error [error-type-param]
     * error-type-param := < (detail-type-descriptor | inferred-type-descriptor) >
     * detail-type-descriptor := type-descriptor
     * inferred-type-descriptor := *
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseErrorTypeDescriptor() {
        STNode errorKeywordToken = parseErrorKeyWord();
        STNode errorTypeParamsNode;
        STToken nextToken = peek();
        STToken nextNextToken = peek(2);
        if (nextToken.kind == SyntaxKind.LT_TOKEN || nextNextToken.kind == SyntaxKind.GT_TOKEN) {
            errorTypeParamsNode = parseErrorTypeParamsNode();
        } else {
            errorTypeParamsNode = STNodeFactory.createEmptyNode();
        }
        return STNodeFactory.createErrorTypeDescriptorNode(errorKeywordToken, errorTypeParamsNode);
    }

    /**
     * Parse error type param node.
     * <p>
     * error-type-param := < (detail-type-descriptor | inferred-type-descriptor) >
     * detail-type-descriptor := type-descriptor
     * inferred-type-descriptor := *
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseErrorTypeParamsNode() {
        STNode ltToken = parseLTToken();
        STNode parameter;
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.ASTERISK_TOKEN) {
            parameter = consume();
        } else {
            parameter = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANGLE_BRACKETS);
        }
        STNode gtToken = parseGTToken();
        return STNodeFactory.createErrorTypeParamsNode(ltToken, parameter, gtToken);
    }

    /**
     * Parse error-keyword.
     *
     * @return Parsed error-keyword node
     */
    private STNode parseErrorKeyWord() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ERROR_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ERROR_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse stream type descriptor.
     * <p>
     * stream-type-descriptor := stream [stream-type-parameters]
     * stream-type-parameters := < type-descriptor [, type-descriptor]>
     * </p>
     *
     * @return Parsed stream type descriptor node
     */
    private STNode parseStreamTypeDescriptor() {
        STNode streamKeywordToken = parseStreamKeyword();
        STNode streamTypeParamsNode;
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.LT_TOKEN) {
            streamTypeParamsNode = parseStreamTypeParamsNode();
        } else {
            streamTypeParamsNode = STNodeFactory.createEmptyNode();
        }
        return STNodeFactory.createStreamTypeDescriptorNode(streamKeywordToken, streamTypeParamsNode);
    }

    /**
     * Parse stream type params node.
     * <p>
     * stream-type-parameters := < type-descriptor [, type-descriptor]>
     * </p>
     *
     * @return Parsed stream type params node
     */
    private STNode parseStreamTypeParamsNode() {
        STNode ltToken = parseLTToken();
        startContext(ParserRuleContext.TYPE_DESC_IN_STREAM_TYPE_DESC);
        STNode leftTypeDescNode = parseTypeDescriptorInternal(ParserRuleContext.TYPE_DESC_IN_STREAM_TYPE_DESC);
        STNode streamTypedesc = parseStreamTypeParamsNode(ltToken, leftTypeDescNode);
        endContext();
        return streamTypedesc;
    }

    private STNode parseStreamTypeParamsNode(STNode ltToken, STNode leftTypeDescNode) {
        return parseStreamTypeParamsNode(peek().kind, ltToken, leftTypeDescNode);
    }

    private STNode parseStreamTypeParamsNode(SyntaxKind nextTokenKind, STNode ltToken, STNode leftTypeDescNode) {
        STNode commaToken, rightTypeDescNode, gtToken;

        switch (nextTokenKind) {
            case COMMA_TOKEN:
                commaToken = parseComma();
                rightTypeDescNode = parseTypeDescriptorInternal(ParserRuleContext.TYPE_DESC_IN_STREAM_TYPE_DESC);
                break;
            case GT_TOKEN:
                commaToken = STNodeFactory.createEmptyNode();
                rightTypeDescNode = STNodeFactory.createEmptyNode();
                break;
            default:
                Solution solution =
                        recover(peek(), ParserRuleContext.STREAM_TYPE_FIRST_PARAM_RHS, ltToken, leftTypeDescNode);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }
                return parseStreamTypeParamsNode(solution.tokenKind, ltToken, leftTypeDescNode);
        }

        gtToken = parseGTToken();
        return STNodeFactory.createStreamTypeParamsNode(ltToken, leftTypeDescNode, commaToken, rightTypeDescNode,
                gtToken);
    }

    /**
     * Parse stream-keyword.
     *
     * @return Parsed stream-keyword node
     */
    private STNode parseStreamKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.STREAM_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.STREAM_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse let expression.
     * <p>
     * <code>
     * let-expr := let let-var-decl [, let-var-decl]* in expression
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseLetExpression(boolean isRhsExpr) {
        STNode letKeyword = parseLetKeyword();
        STNode letVarDeclarations = parseLetVarDeclarations(ParserRuleContext.LET_EXPR_LET_VAR_DECL, isRhsExpr);
        STNode inKeyword = parseInKeyword();

        
        
        STNode expression = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false);
        return STNodeFactory.createLetExpressionNode(letKeyword, letVarDeclarations, inKeyword, expression);
    }

    /**
     * Parse let-keyword.
     *
     * @return Let-keyword node
     */
    private STNode parseLetKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.LET_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.LET_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse let variable declarations.
     * <p>
     * <code>let-var-decl-list := let-var-decl [, let-var-decl]*</code>
     *
     * @return Parsed node
     */
    private STNode parseLetVarDeclarations(ParserRuleContext context, boolean isRhsExpr) {
        startContext(context);
        List<STNode> varDecls = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfLetVarDeclarations(nextToken.kind)) {
            endContext();
            this.errorHandler.reportMissingTokenError("missing let variable declaration");
            return STNodeFactory.createNodeList(varDecls);
        }

        
        STNode varDec = parseLetVarDec(isRhsExpr);
        varDecls.add(varDec);

        
        nextToken = peek();
        STNode leadingComma;
        while (!isEndOfLetVarDeclarations(nextToken.kind)) {
            leadingComma = parseComma();
            varDecls.add(leadingComma);
            varDec = parseLetVarDec(isRhsExpr);
            varDecls.add(varDec);
            nextToken = peek();
        }

        endContext();
        return STNodeFactory.createNodeList(varDecls);
    }

    private boolean isEndOfLetVarDeclarations(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case COMMA_TOKEN:
            case AT_TOKEN:
                return false;
            case IN_KEYWORD:
                return true;
            default:
                return !isTypeStartingToken(tokenKind);
        }
    }

    /**
     * Parse let variable declaration.
     * <p>
     * <code>let-var-decl := [annots] typed-binding-pattern = expression</code>
     *
     * @return Parsed node
     */
    private STNode parseLetVarDec(boolean isRhsExpr) {
        STNode annot = parseAnnotations();
        STNode typedBindingPattern = parseTypedBindingPattern(ParserRuleContext.LET_EXPR_LET_VAR_DECL);
        STNode assign = parseAssignOp();

        
        
        STNode expression = parseExpression(OperatorPrecedence.ANON_FUNC_OR_LET, isRhsExpr, false);
        return STNodeFactory.createLetVariableDeclarationNode(annot, typedBindingPattern, assign, expression);
    }

    /**
     * Parse raw backtick string template expression.
     * <p>
     * <code>BacktickString := `expression`</code>
     *
     * @return Template expression node
     */
    private STNode parseTemplateExpression() {
        STNode type = STNodeFactory.createEmptyNode();
        STNode startingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_START);
        STNode content = parseTemplateContent();
        STNode endingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_START);
        return STNodeFactory.createTemplateExpressionNode(SyntaxKind.RAW_TEMPLATE_EXPRESSION, type, startingBackTick,
                content, endingBackTick);
    }

    private STNode parseTemplateContent() {
        List<STNode> items = new ArrayList<>();
        STToken nextToken = peek();
        while (!isEndOfBacktickContent(nextToken.kind)) {
            STNode contentItem = parseTemplateItem();
            items.add(contentItem);
            nextToken = peek();
        }
        return STNodeFactory.createNodeList(items);
    }

    private boolean isEndOfBacktickContent(SyntaxKind kind) {
        switch (kind) {
            case EOF_TOKEN:
            case BACKTICK_TOKEN:
                return true;
            default:
                return false;
        }
    }

    private STNode parseTemplateItem() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.INTERPOLATION_START_TOKEN) {
            return parseInterpolation();
        }

        
        return consume();
    }

    /**
     * Parse string template expression.
     * <p>
     * <code>string-template-expr := string ` expression `</code>
     *
     * @return String template expression node
     */
    private STNode parseStringTemplateExpression() {
        STNode type = parseStringKeyword();
        STNode startingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_START);
        STNode content = parseTemplateContent();
        STNode endingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_END);
        return STNodeFactory.createTemplateExpressionNode(SyntaxKind.STRING_TEMPLATE_EXPRESSION, type, startingBackTick,
                content, endingBackTick);
    }

    /**
     * Parse <code>string</code> keyword.
     *
     * @return string keyword node
     */
    private STNode parseStringKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.STRING_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.STRING_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse XML template expression.
     * <p>
     * <code>xml-template-expr := xml BacktickString</code>
     *
     * @return XML template expression
     */
    private STNode parseXMLTemplateExpression() {
        STNode xmlKeyword = parseXMLKeyword();
        STNode startingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_START);
        STNode content = parseTemplateContentAsXML();
        STNode endingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_END);
        return STNodeFactory.createTemplateExpressionNode(SyntaxKind.XML_TEMPLATE_EXPRESSION, xmlKeyword,
                startingBackTick, content, endingBackTick);
    }

    /**
     * Parse <code>xml</code> keyword.
     *
     * @return xml keyword node
     */
    private STNode parseXMLKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.XML_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.XML_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse the content of the template string as XML. This method first read the
     * input in the same way as the raw-backtick-template (BacktickString). Then
     * it parses the content as XML.
     *
     * @return XML node
     */
    private STNode parseTemplateContentAsXML() {
        
        
        
        ArrayDeque<STNode> expressions = new ArrayDeque<>();
        StringBuilder xmlStringBuilder = new StringBuilder();
        STToken nextToken = peek();
        while (!isEndOfBacktickContent(nextToken.kind)) {
            STNode contentItem = parseTemplateItem();
            if (contentItem.kind == SyntaxKind.TEMPLATE_STRING) {
                xmlStringBuilder.append(((STToken) contentItem).text());
            } else {
                xmlStringBuilder.append("${}");
                expressions.add(contentItem);
            }
            nextToken = peek();
        }

        TextDocument textDocument = TextDocuments.from(xmlStringBuilder.toString());
        AbstractTokenReader tokenReader = new TokenReader(new XMLLexer(textDocument.getCharacterReader()));
        XMLParser xmlParser = new XMLParser(tokenReader, expressions);
        return xmlParser.parse();
    }

    /**
     * Parse interpolation of a back-tick string.
     * <p>
     * <code>
     * interpolation := ${ expression }
     * </code>
     *
     * @return Interpolation node
     */
    private STNode parseInterpolation() {
        startContext(ParserRuleContext.INTERPOLATION);
        STNode interpolStart = parseInterpolationStart();
        STNode expr = parseExpression();
        removeAdditionalTokensInInterpolation();
        STNode closeBrace = parseCloseBrace();
        endContext();
        return STNodeFactory.createInterpolationNode(interpolStart, expr, closeBrace);
    }

    /**
     * Parse interpolation start token.
     * <p>
     * <code>interpolation-start := ${</code>
     *
     * @return Interpolation start token
     */
    private STNode parseInterpolationStart() {
        STToken token = peek();
        if (token.kind == SyntaxKind.INTERPOLATION_START_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.INTERPOLATION_START_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Remove if there any tokens left after the expression inside the interpolation.
     */
    private void removeAdditionalTokensInInterpolation() {
        while (true) {
            STToken nextToken = peek();
            switch (nextToken.kind) {
                case EOF_TOKEN:
                    return;
                case CLOSE_BRACE_TOKEN:
                    return;
                default:
                    consume();
                    this.errorHandler.reportInvalidNode(nextToken, "invalid token '" + nextToken.text() + "'");
            }
        }
    }

    /**
     * Parse back-tick token.
     *
     * @return Back-tick token
     */
    private STNode parseBacktickToken(ParserRuleContext ctx) {
        STToken token = peek();
        if (token.kind == SyntaxKind.BACKTICK_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ctx);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse table type descriptor.
     * <p>
     * table-type-descriptor := table row-type-parameter [key-constraint]
     * row-type-parameter := type-parameter
     * key-constraint := key-specifier | key-type-constraint
     * key-specifier := key ( [ field-name (, field-name)* ] )
     * key-type-constraint := key type-parameter
     * </p>
     *
     * @return Parsed table type desc node.
     */
    private STNode parseTableTypeDescriptor() {
        STNode tableKeywordToken = parseTableKeyword();
        STNode rowTypeParameterNode = parseRowTypeParameter();
        STNode keyConstraintNode;
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.KEY_KEYWORD) {
            STNode keyKeywordToken = parseKeyKeyword();
            keyConstraintNode = parseKeyConstraint(keyKeywordToken);
        } else {
            keyConstraintNode = STNodeFactory.createEmptyNode();
        }
        return STNodeFactory.createTableTypeDescriptorNode(tableKeywordToken, rowTypeParameterNode, keyConstraintNode);
    }

    /**
     * Parse row type parameter node.
     * <p>
     * row-type-parameter := type-parameter
     * </p>
     *
     * @return Parsed node.
     */
    private STNode parseRowTypeParameter() {
        startContext(ParserRuleContext.ROW_TYPE_PARAM);
        STNode rowTypeParameterNode = parseTypeParameter();
        endContext();
        return rowTypeParameterNode;
    }

    /**
     * Parse type parameter node.
     * <p>
     * type-parameter := < type-descriptor >
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseTypeParameter() {
        STNode ltToken = parseLTToken();
        STNode typeNode = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANGLE_BRACKETS);
        STNode gtToken = parseGTToken();
        return STNodeFactory.createTypeParameterNode(ltToken, typeNode, gtToken);
    }

    /**
     * Parse key constraint.
     * <p>
     * key-constraint := key-specifier | key-type-constraint
     * </p>
     *
     * @return Parsed node.
     */
    private STNode parseKeyConstraint(STNode keyKeywordToken) {
        return parseKeyConstraint(peek().kind, keyKeywordToken);
    }

    private STNode parseKeyConstraint(SyntaxKind nextTokenKind, STNode keyKeywordToken) {
        switch (nextTokenKind) {
            case OPEN_PAREN_TOKEN:
                return parseKeySpecifier(keyKeywordToken);
            case LT_TOKEN:
                return parseKeyTypeConstraint(keyKeywordToken);
            default:
                Solution solution = recover(peek(), ParserRuleContext.KEY_CONSTRAINTS_RHS, keyKeywordToken);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }
                return parseKeyConstraint(solution.tokenKind, keyKeywordToken);
        }
    }

    /**
     * Parse key specifier given parsed key keyword token.
     * <p>
     * <code>key-specifier := key ( [ field-name (, field-name)* ] )</code>
     *
     * @return Parsed node
     */
    private STNode parseKeySpecifier(STNode keyKeywordToken) {
        startContext(ParserRuleContext.KEY_SPECIFIER);
        STNode openParenToken = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
        STNode fieldNamesNode = parseFieldNames();
        STNode closeParenToken = parseCloseParenthesis();
        endContext();
        return STNodeFactory.createKeySpecifierNode(keyKeywordToken, openParenToken, fieldNamesNode, closeParenToken);
    }

    /**
     * Parse key type constraint.
     * <p>
     * key-type-constraint := key type-parameter
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseKeyTypeConstraint(STNode keyKeywordToken) {
        STNode typeParameterNode = parseTypeParameter();
        return STNodeFactory.createKeyTypeConstraintNode(keyKeywordToken, typeParameterNode);
    }

    /**
     * Parse function type descriptor.
     * <p>
     * <code>function-type-descriptor := function function-signature</code>
     *
     * @return Function type descriptor node
     */
    private STNode parseFunctionTypeDesc() {
        startContext(ParserRuleContext.FUNC_TYPE_DESC);
        STNode functionKeyword = parseFunctionKeyword();
        STNode signature = parseFuncSignature(true);
        endContext();
        return STNodeFactory.createFunctionTypeDescriptorNode(functionKeyword, signature);
    }

    /**
     * Parse explicit anonymous function expression.
     * <p>
     * <code>explicit-anonymous-function-expr := [annots] function function-signature anon-func-body</code>
     *
     * @param annots Annotations.
     * @return Anonymous function expression node
     */
    private STNode parseExplicitFunctionExpression(STNode annots) {
        startContext(ParserRuleContext.ANON_FUNC_EXPRESSION);
        STNode funcKeyword = parseFunctionKeyword();
        STNode funcSignature = parseFuncSignature(false);
        STNode funcBody = parseAnonFuncBody();
        return STNodeFactory.createExplicitAnonymousFunctionExpressionNode(annots, funcKeyword, funcSignature,
                funcBody);
    }

    /**
     * Parse anonymous function body.
     * <p>
     * <code>anon-func-body := block-function-body | expr-function-body</code>
     *
     * @return
     */
    private STNode parseAnonFuncBody() {
        return parseAnonFuncBody(peek().kind);
    }

    private STNode parseAnonFuncBody(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case OPEN_BRACE_TOKEN:
            case EOF_TOKEN:
                STNode body = parseFunctionBodyBlock(true);
                endContext();
                return body;
            case RIGHT_DOUBLE_ARROW_TOKEN:
                
                
                endContext();
                return parseExpressionFuncBody(true);
            default:
                Solution solution = recover(peek(), ParserRuleContext.ANON_FUNC_BODY);
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }
                return parseAnonFuncBody(solution.tokenKind);
        }
    }

    /**
     * Parse expression function body.
     * <p>
     * <code>expr-function-body := => expression</code>
     *
     * @return Expression function body node
     */
    private STNode parseExpressionFuncBody(boolean isAnon) {
        STNode rightDoubleArrow = parseDoubleRightArrow();
        STNode expression = parseExpression();

        STNode semiColon;
        if (isAnon) {
            semiColon = STNodeFactory.createEmptyNode();
        } else {
            semiColon = parseSemicolon();
        }
        return STNodeFactory.createExpressionFunctionBodyNode(rightDoubleArrow, expression, semiColon);
    }

    /**
     * Parse '=>' token.
     *
     * @return Double right arrow token
     */
    private STNode parseDoubleRightArrow() {
        STToken token = peek();
        if (token.kind == SyntaxKind.RIGHT_DOUBLE_ARROW_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.EXPR_FUNC_BODY_START);
            return sol.recoveredNode;
        }
    }

    private STNode parseImplicitAnonFunc(STNode params) {
        switch (params.kind) {
            case SIMPLE_NAME_REFERENCE:
            case INFER_PARAM_LIST:
                break;
            case BRACED_EXPRESSION:
                params = getAnonFuncParam((STBracedExpressionNode) params);
                break;
            default:
                this.errorHandler.reportInvalidNode(null, "lhs must be an identifier or a param list");
        }
        STNode rightDoubleArrow = parseDoubleRightArrow();
        STNode expression = parseExpression();
        return STNodeFactory.createImplicitAnonymousFunctionExpressionNode(params, rightDoubleArrow, expression);
    }

    /**
     * Create a new anon-func-param node from a braced expression.
     *
     * @param params Braced expression
     * @return Anon-func param node
     */
    private STNode getAnonFuncParam(STBracedExpressionNode params) {
        List<STNode> paramList = new ArrayList<>();
        paramList.add(params.expression);
        return STNodeFactory.createImplicitAnonymousFunctionParameters(params.openParen,
                STNodeFactory.createNodeList(paramList), params.closeParen);
    }

    /**
     * Parse implicit anon function expression.
     *
     * @param openParen Open parenthesis token
     * @param firstParam First parameter
     * @return Implicit anon function expression node
     */
    private STNode parseImplicitAnonFunc(STNode openParen, STNode firstParam) {
        List<STNode> paramList = new ArrayList<>();
        paramList.add(firstParam);

        
        STToken nextToken = peek();
        STNode paramEnd;
        STNode param;
        while (!isEndOfAnonFuncParametersList(nextToken.kind)) {
            paramEnd = parseImplicitAnonFuncParamEnd(nextToken.kind);
            if (paramEnd == null) {
                break;
            }

            paramList.add(paramEnd);
            param = parseIdentifier(ParserRuleContext.IMPLICIT_ANON_FUNC_PARAM);
            paramList.add(param);
            nextToken = peek();
        }

        STNode params = STNodeFactory.createNodeList(paramList);
        STNode closeParen = parseCloseParenthesis();
        endContext(); 

        STNode inferedParams = STNodeFactory.createImplicitAnonymousFunctionParameters(openParen, params, closeParen);
        return parseImplicitAnonFunc(inferedParams);
    }

    private STNode parseImplicitAnonFuncParamEnd() {
        return parseImplicitAnonFuncParamEnd(peek().kind);
    }

    private STNode parseImplicitAnonFuncParamEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_PAREN_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.ANON_FUNC_PARAM_RHS);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseImplicitAnonFuncParamEnd(solution.tokenKind);
        }
    }

    private boolean isEndOfAnonFuncParametersList(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACE_TOKEN:
            case CLOSE_PAREN_TOKEN:
            case CLOSE_BRACKET_TOKEN:
            case SEMICOLON_TOKEN:
            case RETURNS_KEYWORD:
            case TYPE_KEYWORD:
            case LISTENER_KEYWORD:
            case IF_KEYWORD:
            case WHILE_KEYWORD:
            case OPEN_BRACE_TOKEN:
            case RIGHT_DOUBLE_ARROW_TOKEN:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse tuple type descriptor.
     * <p>
     * <code>tuple-type-descriptor := [ tuple-member-type-descriptors ]
     * <br/><br/>
     * tuple-member-type-descriptors := member-type-descriptor (, member-type-descriptor)* [, tuple-rest-descriptor]
     *                                     | [ tuple-rest-descriptor ]
     * <br/><br/>
     * tuple-rest-descriptor := type-descriptor ...
     * </code>
     *
     * @return
     */
    private STNode parseTupleTypeDesc() {
        STNode openBracket = parseOpenBracket();
        startContext(ParserRuleContext.TYPE_DESC_IN_TUPLE);
        STNode memberTypeDesc = parseTupleMemberTypeDescList();
        STNode closeBracket = parseCloseBracket();
        endContext();
        return STNodeFactory.createTupleTypeDescriptorNode(openBracket, memberTypeDesc, closeBracket);
    }

    /**
     * Parse tuple member type descriptors.
     *
     * @return Parsed node
     */
    private STNode parseTupleMemberTypeDescList() {
        List<STNode> typeDescList = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfTypeList(nextToken.kind)) {
            this.errorHandler.reportMissingTokenError("missing type-desc");
            return STNodeFactory.createNodeList(new ArrayList<>());
        }

        
        STNode typeDesc = parseTypeDescriptorInternal(ParserRuleContext.TYPE_DESC_IN_TUPLE);

        return parseTupleTypeMembers(typeDesc, typeDescList);
    }

    private STNode parseTupleTypeMembers(STNode typeDesc, List<STNode> typeDescList) {
        STToken nextToken;
        
        nextToken = peek();
        STNode tupleMemberRhs;
        while (!isEndOfTypeList(nextToken.kind)) {
            tupleMemberRhs = parseTupleMemberRhs(nextToken.kind);
            if (tupleMemberRhs == null) {
                break;
            }
            if (tupleMemberRhs.kind == SyntaxKind.ELLIPSIS_TOKEN) {
                typeDesc = STNodeFactory.createRestDescriptorNode(typeDesc, tupleMemberRhs);
                break;
            }
            typeDescList.add(typeDesc);
            typeDescList.add(tupleMemberRhs);
            typeDesc = parseTypeDescriptorInternal(ParserRuleContext.TYPE_DESC_IN_TUPLE);
            nextToken = peek();
        }

        typeDescList.add(typeDesc);

        return STNodeFactory.createNodeList(typeDescList);
    }

    private STNode parseTupleMemberRhs() {
        return parseTupleMemberRhs(peek().kind);
    }

    private STNode parseTupleMemberRhs(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACKET_TOKEN:
                return null;
            case ELLIPSIS_TOKEN:
                return parseEllipsis();
            default:
                Solution solution = recover(peek(), ParserRuleContext.TYPE_DESC_IN_TUPLE_RHS);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseTupleMemberRhs(solution.tokenKind);
        }
    }

    private boolean isEndOfTypeList(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case CLOSE_BRACKET_TOKEN:
            case CLOSE_BRACE_TOKEN:
            case CLOSE_PAREN_TOKEN:
            case EOF_TOKEN:
            case EQUAL_TOKEN:
            case OPEN_BRACE_TOKEN:
            case SEMICOLON_TOKEN:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse table constructor or query expression.
     * <p>
     * <code>
     * table-constructor-or-query-expr := table-constructor-expr | query-expr
     * <br/>
     * table-constructor-expr := table [key-specifier] [ [row-list] ]
     * <br/>
     * query-expr := [query-construct-type] query-pipeline select-clause
     * <br/>
     * query-construct-type := table key-specifier | stream
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseTableConstructorOrQuery(boolean isRhsExpr) {
        startContext(ParserRuleContext.TABLE_CONSTRUCTOR_OR_QUERY_EXPRESSION);
        STNode tableOrQueryExpr = parseTableConstructorOrQuery(peek().kind, isRhsExpr);
        endContext();
        return tableOrQueryExpr;
    }

    private STNode parseTableConstructorOrQuery(SyntaxKind nextTokenKind, boolean isRhsExpr) {
        STNode queryConstructType;
        switch (nextTokenKind) {
            case FROM_KEYWORD:
                queryConstructType = STNodeFactory.createEmptyNode();
                return parseQueryExprRhs(queryConstructType, isRhsExpr);
            case STREAM_KEYWORD:
                queryConstructType = parseStreamKeyword();
                return parseQueryExprRhs(queryConstructType, isRhsExpr);
            case TABLE_KEYWORD:
                STNode tableKeyword = parseTableKeyword();
                return parseTableConstructorOrQuery(tableKeyword, isRhsExpr);
            default:
                Solution solution = recover(peek(), ParserRuleContext.TABLE_CONSTRUCTOR_OR_QUERY_START, isRhsExpr);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseTableConstructorOrQuery(solution.tokenKind, isRhsExpr);
        }

    }

    private STNode parseTableConstructorOrQuery(STNode tableKeyword, boolean isRhsExpr) {
        return parseTableConstructorOrQuery(peek().kind, tableKeyword, isRhsExpr);
    }

    private STNode parseTableConstructorOrQuery(SyntaxKind nextTokenKind, STNode tableKeyword, boolean isRhsExpr) {
        STNode keySpecifier;
        switch (nextTokenKind) {
            case OPEN_BRACKET_TOKEN:
                keySpecifier = STNodeFactory.createEmptyNode();
                return parseTableConstructorExprRhs(tableKeyword, keySpecifier);
            case KEY_KEYWORD:
                keySpecifier = parseKeySpecifier();
                return parseTableConstructorOrQueryRhs(tableKeyword, keySpecifier, isRhsExpr);
            default:
                Solution solution = recover(peek(), ParserRuleContext.TABLE_KEYWORD_RHS, tableKeyword, isRhsExpr);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseTableConstructorOrQuery(solution.tokenKind, tableKeyword, isRhsExpr);
        }
    }

    private STNode parseTableConstructorOrQueryRhs(STNode tableKeyword, STNode keySpecifier, boolean isRhsExpr) {
        return parseTableConstructorOrQueryRhs(peek().kind, tableKeyword, keySpecifier, isRhsExpr);
    }

    private STNode parseTableConstructorOrQueryRhs(SyntaxKind nextTokenKind, STNode tableKeyword, STNode keySpecifier,
                                                   boolean isRhsExpr) {
        switch (nextTokenKind) {
            case FROM_KEYWORD:
                return parseQueryExprRhs(parseQueryConstructType(tableKeyword, keySpecifier), isRhsExpr);
            case OPEN_BRACKET_TOKEN:
                return parseTableConstructorExprRhs(tableKeyword, keySpecifier);
            default:
                Solution solution = recover(peek(), ParserRuleContext.TABLE_CONSTRUCTOR_OR_QUERY_RHS, tableKeyword,
                        keySpecifier, isRhsExpr);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseTableConstructorOrQueryRhs(solution.tokenKind, tableKeyword, keySpecifier, isRhsExpr);
        }
    }

    /**
     * Parse query construct type.
     * <p>
     * <code>query-construct-type := table key-specifier</code>
     *
     * @return Parsed node
     */
    private STNode parseQueryConstructType(STNode tableKeyword, STNode keySpecifier) {
        return STNodeFactory.createQueryConstructTypeNode(tableKeyword, keySpecifier);
    }

    /**
     * Parse query expression.
     * <p>
     * <code>
     * query-expr-rhs := query-pipeline select-clause
     * <br/>
     * query-pipeline := from-clause intermediate-clause*
     * </code>
     *
     * @param queryConstructType queryConstructType that precedes this rhs
     * @return Parsed node
     */
    private STNode parseQueryExprRhs(STNode queryConstructType, boolean isRhsExpr) {
        switchContext(ParserRuleContext.QUERY_EXPRESSION);
        STNode fromClause = parseFromClause(isRhsExpr);

        List<STNode> clauses = new ArrayList<>();
        STNode intermediateClause;
        STNode selectClause = null;

        while (!isEndOfIntermediateClause(peek().kind)) {
            intermediateClause = parseIntermediateClause(isRhsExpr);
            if (intermediateClause == null) {
                break;
            }

            
            if (selectClause != null) {
                
                this.errorHandler.reportInvalidNode(null, "extra clauses after select clause");
                break;
            }

            if (intermediateClause.kind == SyntaxKind.SELECT_CLAUSE) {
                selectClause = intermediateClause;
            } else {
                clauses.add(intermediateClause);
            }
        }

        STNode intermediateClauses = STNodeFactory.createNodeList(clauses);
        STNode queryPipeline = STNodeFactory.createQueryPipelineNode(fromClause, intermediateClauses);

        if (peek().kind == SyntaxKind.DO_KEYWORD) {
            return parseQueryAction(queryPipeline, selectClause);
        }

        if (selectClause == null) {
            this.errorHandler.reportMissingTokenError("missing select clause");
            STNode selectKeyword = STNodeFactory.createMissingToken(SyntaxKind.SELECT_KEYWORD);
            STNode expr = STNodeFactory.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN);
            selectClause = STNodeFactory.createSelectClauseNode(selectKeyword, expr);
        }

        return STNodeFactory.createQueryExpressionNode(queryConstructType, queryPipeline, selectClause);
    }

    /**
     * Parse an intermediate clause.
     * <p>
     * <code>
     * intermediate-clause := from-clause | where-clause | let-clause
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseIntermediateClause(boolean isRhsExpr) {
        return parseIntermediateClause(peek().kind, isRhsExpr);
    }

    private STNode parseIntermediateClause(SyntaxKind nextTokenKind, boolean isRhsExpr) {
        switch (nextTokenKind) {
            case FROM_KEYWORD:
                return parseFromClause(isRhsExpr);
            case WHERE_KEYWORD:
                return parseWhereClause(isRhsExpr);
            case LET_KEYWORD:
                return parseLetClause(isRhsExpr);
            case SELECT_KEYWORD:
                return parseSelectClause(isRhsExpr);
            case DO_KEYWORD:
            case SEMICOLON_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.QUERY_PIPELINE_RHS, isRhsExpr);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseIntermediateClause(solution.tokenKind, isRhsExpr);
        }
    }

    private boolean isEndOfIntermediateClause(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case CLOSE_BRACE_TOKEN:
            case CLOSE_PAREN_TOKEN:
            case CLOSE_BRACKET_TOKEN:
            case OPEN_BRACE_TOKEN:
            case SEMICOLON_TOKEN:
            case PUBLIC_KEYWORD:
            case FUNCTION_KEYWORD:
            case EOF_TOKEN:
            case RESOURCE_KEYWORD:
            case LISTENER_KEYWORD:
            case DOCUMENTATION_LINE:
            case PRIVATE_KEYWORD:
            case RETURNS_KEYWORD:
            case SERVICE_KEYWORD:
            case TYPE_KEYWORD:
            case CONST_KEYWORD:
            case FINAL_KEYWORD:
            case DO_KEYWORD:
                return true;
            default:
                return isValidExprRhsStart(tokenKind);
        }
    }

    /**
     * Parse from clause.
     * <p>
     * <code>from-clause := from typed-binding-pattern in expression</code>
     *
     * @return Parsed node
     */
    private STNode parseFromClause(boolean isRhsExpr) {
        STNode fromKeyword = parseFromKeyword();
        
        STNode type = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
        STNode varName = parseVariableName();
        STNode inKeyword = parseInKeyword();

        
        
        STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
        return STNodeFactory.createFromClauseNode(fromKeyword, type, varName, inKeyword, expression);
    }

    /**
     * Parse from-keyword.
     *
     * @return From-keyword node
     */
    private STNode parseFromKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FROM_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FROM_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse where clause.
     * <p>
     * <code>where-clause := where expression</code>
     *
     * @return Parsed node
     */
    private STNode parseWhereClause(boolean isRhsExpr) {
        STNode whereKeyword = parseWhereKeyword();

        
        
        STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
        return STNodeFactory.createWhereClauseNode(whereKeyword, expression);
    }

    /**
     * Parse where-keyword.
     *
     * @return Where-keyword node
     */
    private STNode parseWhereKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.WHERE_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.WHERE_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse let clause.
     * <p>
     * <code>let-clause := let let-var-decl [, let-var-decl]* </code>
     *
     * @return Parsed node
     */
    private STNode parseLetClause(boolean isRhsExpr) {
        STNode letKeyword = parseLetKeyword();
        STNode letVarDeclarations = parseLetVarDeclarations(ParserRuleContext.LET_CLAUSE_LET_VAR_DECL, isRhsExpr);
        return STNodeFactory.createLetClauseNode(letKeyword, letVarDeclarations);
    }

    /**
     * Parse select clause.
     * <p>
     * <code>select-clause := select expression</code>
     *
     * @return Parsed node
     */
    private STNode parseSelectClause(boolean isRhsExpr) {
        STNode selectKeyword = parseSelectKeyword();

        
        
        STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
        return STNodeFactory.createSelectClauseNode(selectKeyword, expression);
    }

    /**
     * Parse select-keyword.
     *
     * @return Select-keyword node
     */
    private STNode parseSelectKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.SELECT_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.SELECT_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse start action.
     * <p>
     * <code>start-action := [annots] start (function-call-expr|method-call-expr|remote-method-call-action)</code>
     *
     * @return Start action node
     */
    private STNode parseStartAction(STNode annots) {
        STNode startKeyword = parseStartKeyword();
        STNode expr = parseActionOrExpression();
        validateExprInStartAction(expr);
        return STNodeFactory.createStartActionNode(annots, startKeyword, expr);
    }

    /**
     * Parse start keyword.
     *
     * @return Start keyword node
     */
    private STNode parseStartKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.START_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.START_KEYWORD);
            return sol.recoveredNode;
        }
    }

    private void validateExprInStartAction(STNode expression) {
        switch (expression.kind) {
            case FUNCTION_CALL:
            case METHOD_CALL:
            case REMOTE_METHOD_CALL_ACTION:
                break;
            default:
                if (isMissingNode(expression)) {
                    break;
                }

                this.errorHandler.reportInvalidNode(null, "expression followed by the start keyword must be a " +
                        "func-call, a method-call or a remote-method-call");
                break;
        }
    }

    /**
     * Parse flush action.
     * <p>
     * <code>flush-action := flush [peer-worker]</code>
     *
     * @return flush action node
     */
    private STNode parseFlushAction() {
        STNode flushKeyword = parseFlushKeyword();
        STNode peerWorker = parseOptionalPeerWorkerName();
        return STNodeFactory.createFlushActionNode(flushKeyword, peerWorker);
    }

    /**
     * Parse flush keyword.
     *
     * @return flush keyword node
     */
    private STNode parseFlushKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FLUSH_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FLUSH_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse peer worker.
     * <p>
     * <code>peer-worker := worker-name | default</code>
     *
     * @return peer worker name node
     */
    private STNode parseOptionalPeerWorkerName() {
        STToken token = peek();
        switch (token.kind) {
            case IDENTIFIER_TOKEN:
            case DEFAULT_KEYWORD:
                return STNodeFactory.createSimpleNameReferenceNode(consume());
            default:
                return STNodeFactory.createEmptyNode();
        }
    }

    /**
     * Parse intersection type descriptor.
     * <p>
     * intersection-type-descriptor := type-descriptor & type-descriptor
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseIntersectionTypeDescriptor(STNode leftTypeDesc, ParserRuleContext context) {
        
        STNode bitwiseAndToken = consume();
        STNode rightTypeDesc = parseTypeDescriptor(context);
        return STNodeFactory.createIntersectionTypeDescriptorNode(leftTypeDesc, bitwiseAndToken, rightTypeDesc);
    }

    /**
     * Parse singleton type descriptor.
     * <p>
     * singleton-type-descriptor := simple-const-expr
     * simple-const-expr :=
     * nil-literal
     * | boolean-literal
     * | [Sign] int-literal
     * | [Sign] floating-point-literal
     * | string-literal
     * | constant-reference-expr
     * </p>
     */
    private STNode parseSingletonTypeDesc() {
        STNode simpleContExpr = parseConstExpr();
        return STNodeFactory.createSingletonTypeDescriptorNode(simpleContExpr);
    }

    private STNode parseSignedIntOrFloat() {
        STNode operator = parseUnaryOperator();
        STNode literal;
        STToken nextToken = peek();
        switch (nextToken.kind) {
            case HEX_INTEGER_LITERAL:
            case DECIMAL_FLOATING_POINT_LITERAL:
            case HEX_FLOATING_POINT_LITERAL:
                literal = consume();
                break;
            default: 
                literal = parseDecimalIntLiteral(ParserRuleContext.DECIMAL_INTEGER_LITERAL);
        }
        return STNodeFactory.createUnaryExpressionNode(operator, literal);
    }

    private boolean isSingletonTypeDescStart(SyntaxKind tokenKind, boolean inTypeDescCtx) {
        STToken nextNextToken = getNextNextToken(tokenKind);
        switch (tokenKind) {
            case STRING_LITERAL:
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case DECIMAL_FLOATING_POINT_LITERAL:
            case HEX_FLOATING_POINT_LITERAL:
            case TRUE_KEYWORD:
            case FALSE_KEYWORD:
            case NULL_KEYWORD:
                if (inTypeDescCtx || isValidTypeDescRHSOutSideTypeDescCtx(nextNextToken)) {
                    return true;
                }
                return false;
            case PLUS_TOKEN:
            case MINUS_TOKEN:
                return isIntOrFloat(nextNextToken);
            default:
                return false;
        }
    }

    static boolean isIntOrFloat(STToken token) {
        switch (token.kind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case DECIMAL_FLOATING_POINT_LITERAL:
            case HEX_FLOATING_POINT_LITERAL:
                return true;
            default:
                return false;
        }
    }

    private boolean isValidTypeDescRHSOutSideTypeDescCtx(STToken token) {
        switch (token.kind) {
            case IDENTIFIER_TOKEN:
            case QUESTION_MARK_TOKEN:
            case OPEN_PAREN_TOKEN:
            case OPEN_BRACKET_TOKEN:
            case PIPE_TOKEN:
            case BITWISE_AND_TOKEN:
            case OPEN_BRACE_TOKEN:
            case ERROR_KEYWORD:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse binding-patterns.
     *
     * binding-pattern :=
     * capture-binding-pattern
     * | wildcard-binding-pattern
     * | list-binding-pattern
     * | mapping-binding-pattern
     * | functional-binding-pattern
     * capture-binding-pattern := variable-name
     * variable-name := identifier
     * wildcard-binding-pattern := _
     * list-binding-pattern := [ list-member-binding-patterns ]
     * list-member-binding-patterns :=
     * binding-pattern (, binding-pattern)* [, rest-binding-pattern]
     * | [ rest-binding-pattern ]
     * mapping-binding-pattern := { field-binding-patterns }
     * field-binding-patterns :=
     * field-binding-pattern (, field-binding-pattern)* [, rest-binding-pattern]
     * | [ rest-binding-pattern ]
     * field-binding-pattern :=
     * field-name : binding-pattern
     * | variable-name
     * rest-binding-pattern := ... variable-name
     * functional-binding-pattern := functionally-constructible-type-reference ( arg-list-binding-pattern )
     * arg-list-binding-pattern :=
     * positional-arg-binding-patterns [, other-arg-binding-patterns]
     * | other-arg-binding-patterns
     * positional-arg-binding-patterns := positional-arg-binding-pattern (, positional-arg-binding-pattern)*
     * positional-arg-binding-pattern := binding-pattern
     * other-arg-binding-patterns :=
     * named-arg-binding-patterns [, rest-binding-pattern]
     * | [rest-binding-pattern]
     * named-arg-binding-patterns := named-arg-binding-pattern (, named-arg-binding-pattern)*
     * named-arg-binding-pattern := arg-name = binding-pattern
     *
     * @return binding-pattern node
     */
    private STNode parseBindingPattern() {
        STToken token = peek();
        return parseBindingPattern(token.kind);
    }

    private STNode parseBindingPattern(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case OPEN_BRACKET_TOKEN:
                return parseListBindingPattern();
            case IDENTIFIER_TOKEN:
                return parseCaptureBindingPattern();
            default:
                Solution sol = recover(peek(), ParserRuleContext.BINDING_PATTERN);

                
                
                
                if (sol.action == Action.REMOVE) {
                    return sol.recoveredNode;
                }

                return parseBindingPattern(sol.tokenKind);
        }
    }

    /**
     * Parse capture-binding-pattern.
     *
     * capture-binding-pattern := variable-name
     * variable-name := identifier
     *
     * @return capture-binding-pattern node
     */
    private STNode parseCaptureBindingPattern() {
        STToken token = peek();
        if (token.kind == SyntaxKind.IDENTIFIER_TOKEN) {
            STNode varName = parseVariableName();
            return STNodeFactory.createCaptureBindingPatternNode(varName);
        } else {
            Solution sol = recover(token, ParserRuleContext.CAPTURE_BINDING_PATTERN);
            if (sol.action == Action.REMOVE) {
                return sol.recoveredNode;
            }

            return STNodeFactory.createCaptureBindingPatternNode(sol.recoveredNode);
        }
    }

    /**
     * Parse list-binding-patterns.
     *
     * list-binding-pattern := [ list-member-binding-patterns ]
     * list-member-binding-patterns :=
     * binding-pattern (, binding-pattern)* [, rest-binding-pattern]
     * | [ rest-binding-pattern ]
     *
     * @return list-binding-pattern node
     */
    private STNode parseListBindingPattern() {
        startContext(ParserRuleContext.LIST_BINDING_PATTERN);
        STNode openBracket = parseOpenBracket();
        List<STNode> bindingPatternsList = new ArrayList<>();
        STNode listBindingPattern = parseListBindingPattern(openBracket, bindingPatternsList);
        endContext();
        return listBindingPattern;
    }

    private STNode parseListBindingPattern(STNode openBracket, List<STNode> bindingPatternsList) {
        STNode listBindingPatternMember = parseListBindingPatternMember();
        bindingPatternsList.add(listBindingPatternMember);
        STNode listBindingPattern = parseListBindingPattern(openBracket, listBindingPatternMember, bindingPatternsList);
        return listBindingPattern;
    }

    private STNode parseListBindingPattern(STNode openBracket, STNode firstMember, List<STNode> bindingPatterns) {
        STNode member = firstMember;
        
        STToken token = peek(); 
        STNode listBindingPatternRhs = null;
        while (!isEndOfListBindingPattern(token.kind) && member.kind != SyntaxKind.REST_BINDING_PATTERN) {
            listBindingPatternRhs = parseListBindingpatternRhs(token.kind);
            if (listBindingPatternRhs == null) {
                break;
            }

            bindingPatterns.add(listBindingPatternRhs);
            member = parseListBindingPatternMember();
            bindingPatterns.add(member);
            token = peek();
        }

        
        STNode restBindingPattern;
        if (member.kind == SyntaxKind.REST_BINDING_PATTERN) {
            restBindingPattern = bindingPatterns.remove(bindingPatterns.size() - 1);
        } else {
            restBindingPattern = STNodeFactory.createEmptyNode();
        }

        STNode closeBracket = parseCloseBracket();
        STNode bindingPatternsNode = STNodeFactory.createNodeList(bindingPatterns);
        return STNodeFactory.createListBindingPatternNode(openBracket, bindingPatternsNode, restBindingPattern,
                closeBracket);
    }

    private STNode parseListBindingpatternRhs() {
        return parseListBindingpatternRhs(peek().kind);
    }

    private STNode parseListBindingpatternRhs(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACKET_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.LIST_BINDING_PATTERN_END_OR_CONTINUE);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseListBindingpatternRhs(solution.tokenKind);
        }
    }

    private boolean isEndOfListBindingPattern(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case CLOSE_BRACKET_TOKEN:
            case EOF_TOKEN:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse list-binding-pattern entry.
     *
     * list-binding-pattern := [ list-member-binding-patterns ]
     * list-member-binding-patterns :=
     * binding-pattern (, binding-pattern)* [, rest-binding-pattern]
     * | [ rest-binding-pattern ]
     *
     * @return rest-binding-pattern node
     */
    private STNode parseListBindingPatternMember() {
        STToken token = peek();
        switch (token.kind) {
            case ELLIPSIS_TOKEN:
                startContext(ParserRuleContext.REST_BINDING_PATTERN);
                STNode ellipsis = parseEllipsis();
                STNode varName = parseVariableName();
                endContext();
                return STNodeFactory.createRestBindingPatternNode(ellipsis, varName);
            default:
                return parseBindingPattern();
        }
    }

    /**
     * Parse Typed-binding-pattern.
     * <p>
     * <code>
     * typed-binding-pattern := inferable-type-descriptor binding-pattern
     * <br/>
     * inferable-type-descriptor := type-descriptor | var
     * </code>
     *
     * @return Typed binding pattern node
     */
    private STNode parseTypedBindingPattern(ParserRuleContext context) {
        STNode typeDesc = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN, true);
        STNode typeBindingPattern = parseTypedBindingPatternTypeRhs(typeDesc, context);
        return typeBindingPattern;
    }

    /**
     * Check whether the parser reached to a valid expression start.
     *
     * @param nextTokenKind Kind of the next immediate token.
     * @param nextTokenIndex Index to the next token.
     * @return <code>true</code> if this is a start of a valid expression. <code>false</code> otherwise
     */
    private boolean isValidExpressionStart(SyntaxKind nextTokenKind, int nextTokenIndex) {
        switch (nextTokenKind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case STRING_LITERAL:
            case NULL_KEYWORD:
            case TRUE_KEYWORD:
            case FALSE_KEYWORD:
            case DECIMAL_FLOATING_POINT_LITERAL:
            case HEX_FLOATING_POINT_LITERAL:
            case IDENTIFIER_TOKEN:
                return isValidExprRhsStart(peek(nextTokenIndex + 1).kind);

            case OPEN_PAREN_TOKEN:
            case CHECK_KEYWORD:
            case CHECKPANIC_KEYWORD:
            case OPEN_BRACE_TOKEN:
            case TYPEOF_KEYWORD:
            case NEGATION_TOKEN:
            case EXCLAMATION_MARK_TOKEN:
            case TRAP_KEYWORD:
            case OPEN_BRACKET_TOKEN:
            case LT_TOKEN:
            case TABLE_KEYWORD:
            case STREAM_KEYWORD:
            case FROM_KEYWORD:
            case ERROR_KEYWORD:
            case LET_KEYWORD:
            case BACKTICK_TOKEN:
            case XML_KEYWORD:
            case STRING_KEYWORD:
            case FUNCTION_KEYWORD:
            case NEW_KEYWORD:
            case LEFT_ARROW_TOKEN:
            case SERVICE_KEYWORD:
                return true;
            case PLUS_TOKEN:
            case MINUS_TOKEN:
                return isValidExpressionStart(peek(nextTokenIndex).kind, nextTokenIndex + 1);

            
            case START_KEYWORD:
            case FLUSH_KEYWORD:
            case WAIT_KEYWORD:
            default:
                return false;
        }
    }

    /**
     * Parse sync send action.
     * <p>
     * <code>sync-send-action := expression ->> peer-worker</code>
     *
     * @param expression LHS expression of the sync send action
     * @return Sync send action node
     */
    private STNode parseSyncSendAction(STNode expression) {
        STNode syncSendToken = parseSyncSendToken();
        STNode peerWorker = parsePeerWorkerName();
        return STNodeFactory.createSyncSendActionNode(expression, syncSendToken, peerWorker);
    }

    /**
     * Parse peer worker.
     * <p>
     * <code>peer-worker := worker-name | default</code>
     *
     * @return peer worker name node
     */
    private STNode parsePeerWorkerName() {
        STToken token = peek();
        switch (token.kind) {
            case IDENTIFIER_TOKEN:
            case DEFAULT_KEYWORD:
                return STNodeFactory.createSimpleNameReferenceNode(consume());
            default:
                Solution sol = recover(token, ParserRuleContext.PEER_WORKER_NAME);
                return sol.recoveredNode;
        }
    }

    /**
     * Parse sync send token.
     * <p>
     * <code>sync-send-token :=  ->> </code>
     *
     * @return sync send token
     */
    private STNode parseSyncSendToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.SYNC_SEND_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.SYNC_SEND_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse receive action.
     * <p>
     * <code>receive-action := single-receive-action | multiple-receive-action</code>
     *
     * @return Receive action
     */
    private STNode parseReceiveAction() {
        STNode leftArrow = parseLeftArrowToken();
        STNode receiveWorkers = parseReceiveWorkers();
        return STNodeFactory.createReceiveActionNode(leftArrow, receiveWorkers);
    }

    private STNode parseReceiveWorkers() {
        return parseReceiveWorkers(peek().kind);
    }

    private STNode parseReceiveWorkers(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case DEFAULT_KEYWORD:
            case IDENTIFIER_TOKEN:
                return parsePeerWorkerName();
            case OPEN_BRACE_TOKEN:
                return parseMultipleReceiveWorkers();
            default:
                Solution solution = recover(peek(), ParserRuleContext.RECEIVE_WORKERS);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseReceiveWorkers(solution.tokenKind);
        }
    }

    /**
     * Parse multiple worker receivers.
     * <p>
     * <code>{ receive-field (, receive-field)* }</code>
     *
     * @return Multiple worker receiver node
     */
    private STNode parseMultipleReceiveWorkers() {
        startContext(ParserRuleContext.MULTI_RECEIVE_WORKERS);
        STNode openBrace = parseOpenBrace();
        STNode receiveFields = parseReceiveFields();
        STNode closeBrace = parseCloseBrace();
        endContext();
        return STNodeFactory.createReceiveFieldsNode(openBrace, receiveFields, closeBrace);
    }

    private STNode parseReceiveFields() {
        List<STNode> receiveFields = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfReceiveFields(nextToken.kind)) {
            this.errorHandler.reportMissingTokenError("missing receive field");
            return STNodeFactory.createNodeList(new ArrayList<>());
        }

        
        STNode receiveField = parseReceiveField();
        receiveFields.add(receiveField);

        
        nextToken = peek();
        STNode recieveFieldEnd;
        while (!isEndOfReceiveFields(nextToken.kind)) {
            recieveFieldEnd = parseReceiveFieldEnd(nextToken.kind);
            if (recieveFieldEnd == null) {
                break;
            }

            receiveFields.add(recieveFieldEnd);
            receiveField = parseReceiveField();
            receiveFields.add(receiveField);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(receiveFields);
    }

    private boolean isEndOfReceiveFields(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACKET_TOKEN:
                return true;
            default:
                return false;
        }
    }

    private STNode parseReceiveFieldEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACE_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.RECEIVE_FIELD_END);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseReceiveFieldEnd(solution.tokenKind);
        }
    }

    private STNode parseReceiveField() {
        return parseReceiveField(peek().kind);
    }

    /**
     * Parse receive field.
     * <p>
     * <code>receive-field := peer-worker | field-name : peer-worker</code>
     *
     * @param nextTokenKind Kind of the next token
     * @return Receiver field node
     */
    private STNode parseReceiveField(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case DEFAULT_KEYWORD:
                return parseDefaultKeyword();
            case IDENTIFIER_TOKEN:
                STNode identifier = parseIdentifier(ParserRuleContext.RECEIVE_FIELD_NAME);
                return createQualifiedReceiveField(identifier);
            default:
                Solution solution = recover(peek(), ParserRuleContext.RECEIVE_FIELD);

                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                if (solution.tokenKind == SyntaxKind.IDENTIFIER_TOKEN) {
                    return createQualifiedReceiveField(solution.recoveredNode);
                }

                return solution.recoveredNode;
        }
    }

    private STNode createQualifiedReceiveField(STNode identifier) {
        if (peek().kind != SyntaxKind.COLON_TOKEN) {
            return identifier;
        }

        STNode colon = parseColon();
        STNode peerWorker = parsePeerWorkerName();
        return STNodeFactory.createQualifiedNameReferenceNode(identifier, colon, peerWorker);
    }

    /**
     *
     * Parse left arrow (<-) token.
     *
     * @return left arrow token
     */
    private STNode parseLeftArrowToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.LEFT_ARROW_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.LEFT_ARROW_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse signed right shift token (>>).
     *
     * @return Parsed node
     */
    private STNode parseSignedRightShiftToken() {
        STNode openGTToken = parseGTToken();
        validateRightShiftOperatorWS(openGTToken);

        STNode endLGToken = parseGTToken();
        return STNodeFactory.createDoubleGTTokenNode(openGTToken, endLGToken);
    }

    /**
     * Parse unsigned right shift token (>>>).
     *
     * @return Parsed node
     */
    private STNode parseUnsignedRightShiftToken() {
        STNode openGTToken = parseGTToken();
        validateRightShiftOperatorWS(openGTToken);

        STNode middleGTToken = parseGTToken();
        validateRightShiftOperatorWS(middleGTToken);

        STNode endLGToken = parseGTToken();
        return STNodeFactory.createTrippleGTTokenNode(openGTToken, middleGTToken, endLGToken);
    }

    /**
     * Validate the whitespace between '>' tokens of right shift operators.
     *
     * @param node Preceding node
     */
    private void validateRightShiftOperatorWS(STNode node) {
        int diff = node.widthWithTrailingMinutiae() - node.width();
        if (diff > 0) {
            this.errorHandler.reportMissingTokenError("no whitespaces allowed between >>");
        }
    }

    /**
     * Parse wait action.
     * <p>
     * <code>wait-action := single-wait-action | multiple-wait-action | alternate-wait-action </code>
     *
     * @return Wait action node
     */
    private STNode parseWaitAction() {
        STNode waitKeyword = parseWaitKeyword();
        if (peek().kind == SyntaxKind.OPEN_BRACE_TOKEN) {
            return parseMultiWaitAction(waitKeyword);
        }

        return parseSingleOrAlternateWaitAction(waitKeyword);
    }

    /**
     * Parse wait keyword.
     *
     * @return wait keyword
     */
    private STNode parseWaitKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.WAIT_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.WAIT_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse single or alternate wait actions.
     * <p>
     * <code>
     * alternate-or-single-wait-action := wait wait-future-expr (| wait-future-expr)+
     * <br/>
     * wait-future-expr := expression but not mapping-constructor-expr
     * </code>
     *
     * @param waitKeyword wait keyword
     * @return Single or alternate wait action node
     */
    private STNode parseSingleOrAlternateWaitAction(STNode waitKeyword) {
        startContext(ParserRuleContext.ALTERNATE_WAIT_EXPRS);
        List<STNode> waitFutureExprList = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfWaitFutureExprList(nextToken.kind)) {
            this.errorHandler.reportMissingTokenError("missing wait field");
            endContext();
            STNode waitFutureExprs = STNodeFactory.createNodeList(waitFutureExprList);
            return STNodeFactory.createWaitActionNode(waitKeyword, waitFutureExprs);
        }

        
        STNode waitField = parseWaitFutureExpr();
        waitFutureExprList.add(waitField);

        
        nextToken = peek();
        STNode waitFutureExprEnd;
        while (!isEndOfWaitFutureExprList(nextToken.kind)) {
            waitFutureExprEnd = parseWaitFutureExprEnd(nextToken.kind, 1);
            if (waitFutureExprEnd == null) {
                break;
            }

            waitFutureExprList.add(waitFutureExprEnd);
            waitField = parseWaitFutureExpr();
            waitFutureExprList.add(waitField);
            nextToken = peek();
        }

        STNode waitFutureExprs = STNodeFactory.createNodeList(waitFutureExprList);
        endContext();
        return STNodeFactory.createWaitActionNode(waitKeyword, waitFutureExprs);
    }

    private boolean isEndOfWaitFutureExprList(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACE_TOKEN:
            case SEMICOLON_TOKEN:
                return true;
            default:
                return false;
        }
    }

    private STNode parseWaitFutureExpr() {
        STNode waitFutureExpr = parseExpression();
        if (waitFutureExpr.kind == SyntaxKind.MAPPING_CONSTRUCTOR) {
            this.errorHandler.reportInvalidNode(null, "mapping constructor expression cannot use as å wait expression");
        }
        return waitFutureExpr;
    }

    private STNode parseWaitFutureExprEnd(int nextTokenIndex) {
        return parseWaitFutureExprEnd(peek().kind, 1);
    }

    private STNode parseWaitFutureExprEnd(SyntaxKind nextTokenKind, int nextTokenIndex) {
        switch (nextTokenKind) {
            case PIPE_TOKEN:
                return parsePipeToken();
            default:
                if (isEndOfWaitFutureExprList(nextTokenKind) ||
                        !isValidExpressionStart(nextTokenKind, nextTokenIndex)) {
                    return null;
                }

                Solution solution = recover(peek(), ParserRuleContext.WAIT_FUTURE_EXPR_END, nextTokenIndex);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                
                return parseWaitFutureExprEnd(solution.tokenKind, 0);
        }
    }

    /**
     * Parse multiple wait action.
     * <p>
     * <code>multiple-wait-action := wait { wait-field (, wait-field)* }</code>
     *
     * @param waitKeyword Wait keyword
     * @return Multiple wait action node
     */
    private STNode parseMultiWaitAction(STNode waitKeyword) {
        startContext(ParserRuleContext.MULTI_WAIT_FIELDS);
        STNode openBrace = parseOpenBrace();
        STNode waitFields = parseWaitFields();
        STNode closeBrace = parseCloseBrace();
        endContext();

        STNode waitFieldsNode = STNodeFactory.createWaitFieldsListNode(openBrace, waitFields, closeBrace);
        return STNodeFactory.createWaitActionNode(waitKeyword, waitFieldsNode);
    }

    private STNode parseWaitFields() {
        List<STNode> waitFields = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfReceiveFields(nextToken.kind)) {
            this.errorHandler.reportMissingTokenError("missing wait field");
            return STNodeFactory.createEmptyNodeList();
        }

        
        STNode waitField = parseWaitField();
        waitFields.add(waitField);

        
        nextToken = peek();
        STNode waitFieldEnd;
        while (!isEndOfReceiveFields(nextToken.kind)) {
            waitFieldEnd = parseWaitFieldEnd(nextToken.kind);
            if (waitFieldEnd == null) {
                break;
            }

            waitFields.add(waitFieldEnd);
            waitField = parseWaitField();
            waitFields.add(waitField);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(waitFields);
    }

    private STNode parseWaitFieldEnd() {
        return parseWaitFieldEnd(peek().kind);
    }

    private STNode parseWaitFieldEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACE_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.WAIT_FIELD_END);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseWaitFieldEnd(solution.tokenKind);
        }
    }

    private STNode parseWaitField() {
        return parseWaitField(peek().kind);
    }

    /**
     * Parse wait field.
     * <p>
     * <code>wait-field := variable-name | field-name : wait-future-expr</code>
     *
     * @param nextTokenKind Kind of the next token
     * @return Receiver field node
     */
    private STNode parseWaitField(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN:
                STNode identifier = parseIdentifier(ParserRuleContext.WAIT_FIELD_NAME);
                return createQualifiedWaitField(identifier);
            default:
                Solution solution = recover(peek(), ParserRuleContext.WAIT_FIELD_NAME);

                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseWaitField(solution.tokenKind);
        }
    }

    private STNode createQualifiedWaitField(STNode identifier) {
        if (peek().kind != SyntaxKind.COLON_TOKEN) {
            return identifier;
        }

        STNode colon = parseColon();
        STNode waitFutureExpr = parseWaitFutureExpr();
        return STNodeFactory.createWaitFieldNode(identifier, colon, waitFutureExpr);
    }

    /**
     * Parse annot access expression.
     * <p>
     * <code>
     * annot-access-expr := expression .@ annot-tag-reference
     * <br/>
     * annot-tag-reference := qualified-identifier | identifier
     * </code>
     *
     * @param lhsExpr Preceding expression of the annot access access
     * @return Parsed node
     */
    private STNode parseAnnotAccessExpression(STNode lhsExpr) {
        STNode annotAccessToken = parseAnnotChainingToken();
        STNode annotTagReference = parseFieldAccessIdentifier();
        return STNodeFactory.createAnnotAccessExpressionNode(lhsExpr, annotAccessToken, annotTagReference);
    }

    /**
     * Parse annot-chaining-token.
     *
     * @return Parsed node
     */
    private STNode parseAnnotChainingToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ANNOT_CHAINING_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ANNOT_CHAINING_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse field access identifier.
     * <p>
     * <code>field-access-identifier := qualified-identifier | identifier</code>
     *
     * @return Parsed node
     */
    private STNode parseFieldAccessIdentifier() {
        return parseQualifiedIdentifier(ParserRuleContext.FIELD_ACCESS_IDENTIFIER);
    }

    /**
     * Parse query action.
     * <p>
     * <code>query-action := query-pipeline do-clause
     * <br/>
     * do-clause := do block-stmt
     * </code>
     *
     * @param queryPipeline Query pipeline
     * @param selectClause Select clause if any This is only for validation.
     * @return Query action node
     */
    private STNode parseQueryAction(STNode queryPipeline, STNode selectClause) {
        if (selectClause != null) {
            this.errorHandler.reportInvalidNode(null, "cannot have a select clause in query action");
        }

        startContext(ParserRuleContext.DO_CLAUSE);
        STNode doKeyword = parseDoKeyword();
        STNode blockStmt = parseBlockNode();
        endContext();
        return STNodeFactory.createQueryActionNode(queryPipeline, doKeyword, blockStmt);
    }

    /**
     * Parse 'do' keyword.
     *
     * @return do keyword node
     */
    private STNode parseDoKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.DO_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.DO_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse optional field access or xml optional attribute access expression.
     * <p>
     * <code>
     * optional-field-access-expr := expression ?. field-name
     * <br/>
     * xml-optional-attribute-access-expr := expression ?. xml-attribute-name
     * <br/>
     * xml-attribute-name := xml-qualified-name | qualified-identifier | identifier
     * <br/>
     * xml-qualified-name := xml-namespace-prefix : identifier
     * <br/>
     * xml-namespace-prefix := identifier
     * </code>
     *
     * @param lhsExpr Preceding expression of the optional access
     * @return Parsed node
     */
    private STNode parseOptionalFieldAccessExpression(STNode lhsExpr) {
        STNode optionalFieldAccessToken = parseOptionalChainingToken();
        STNode fieldName = parseFieldAccessIdentifier();
        return STNodeFactory.createOptionalFieldAccessExpressionNode(lhsExpr, optionalFieldAccessToken, fieldName);
    }

    /**
     * Parse optional chaining token.
     *
     * @return parsed node
     */
    private STNode parseOptionalChainingToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.OPTIONAL_CHAINING_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.OPTIONAL_CHAINING_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse conditional expression.
     * <p>
     * <code>conditional-expr := expression ? expression : expression</code>
     *
     * @param lhsExpr Preceding expression of the question mark
     * @return Parsed node
     */
    private STNode parseConditionalExpression(STNode lhsExpr) {
        startContext(ParserRuleContext.CONDITIONAL_EXPRESSION);
        STNode questionMark = parseQuestionMark();
        STNode middleExpr = parseExpression(OperatorPrecedence.ELVIS_CONDITIONAL, true, false);
        STNode colon = parseColon();
        endContext();
        STNode endExpr = parseExpression(OperatorPrecedence.ELVIS_CONDITIONAL, true, false);
        return STNodeFactory.createConditionalExpressionNode(lhsExpr, questionMark, middleExpr, colon, endExpr);
    }

    /**
     * Parse enum declaration.
     * <p>
     * module-enum-decl :=
     * metadata
     * [public] enum identifier { enum-member (, enum-member)* }
     * enum-member := metadata identifier [= const-expr]
     * </p>
     *
     * @param metadata
     * @param qualifier
     *
     * @return Parsed enum node.
     */
    private STNode parseEnumDeclaration(STNode metadata, STNode qualifier) {
        startContext(ParserRuleContext.MODULE_ENUM_DECLARATION);
        STNode enumKeywordToken = parseEnumKeyword();
        STNode identifier = parseIdentifier(ParserRuleContext.MODULE_ENUM_NAME);
        STNode openBraceToken = parseOpenBrace();
        STNode enumMemberList = parseEnumMemberList();
        STNode closeBraceToken = parseCloseBrace();

        endContext();
        return STNodeFactory.createEnumDeclarationNode(metadata, qualifier, enumKeywordToken, identifier,
                openBraceToken, enumMemberList, closeBraceToken);
    }

    /**
     * Parse 'enum' keyword.
     *
     * @return enum keyword node
     */
    private STNode parseEnumKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ENUM_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ENUM_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse enum member list.
     * <p>
     * enum-member := metadata identifier [= const-expr]
     * </p>
     *
     * @return enum member list node.
     */
    private STNode parseEnumMemberList() {
        startContext(ParserRuleContext.ENUM_MEMBER_LIST);
        List<STNode> enumMemberList = new ArrayList<>();
        STToken nextToken = peek();

        
        if (nextToken.kind == SyntaxKind.CLOSE_BRACE_TOKEN) {
            this.errorHandler.reportMissingTokenError("enum member list cannot be empty");
            return STNodeFactory.createNodeList(new ArrayList<>());
        }

        
        STNode enumMember = parseEnumMember();

        
        nextToken = peek();
        STNode enumMemberRhs;
        while (nextToken.kind != SyntaxKind.CLOSE_BRACE_TOKEN) {
            enumMemberRhs = parseEnumMemberRhs(nextToken.kind);
            if (enumMemberRhs == null) {
                break;
            }
            enumMemberList.add(enumMember);
            enumMemberList.add(enumMemberRhs);
            enumMember = parseEnumMember();
            nextToken = peek();
        }

        enumMemberList.add(enumMember);

        endContext();
        return STNodeFactory.createNodeList(enumMemberList);
    }

    /**
     * Parse enum member.
     * <p>
     * enum-member := metadata identifier [= const-expr]
     * </p>
     *
     * @return Parsed enum member node.
     */
    private STNode parseEnumMember() {
        STToken nextToken = peek();
        STNode metadata;
        switch (nextToken.kind) {
            case DOCUMENTATION_LINE:
            case AT_TOKEN:
                metadata = parseMetaData(nextToken.kind);
                break;
            default:
                metadata = STNodeFactory.createEmptyNode();
        }

        STNode identifierNode = parseIdentifier(ParserRuleContext.ENUM_MEMBER_NAME);
        return parseEnumMemberInternalRhs(metadata, identifierNode);
    }

    private STNode parseEnumMemberInternalRhs(STNode metadata, STNode identifierNode) {
        return parseEnumMemberInternalRhs(metadata, identifierNode, peek().kind);
    }

    private STNode parseEnumMemberInternalRhs(STNode metadata, STNode identifierNode, SyntaxKind nextToken) {
        STNode equalToken, constExprNode;
        switch (nextToken) {
            case EQUAL_TOKEN:
                equalToken = parseAssignOp();
                constExprNode = parseExpression();
                break;
            case COMMA_TOKEN:
            case CLOSE_BRACE_TOKEN:
                equalToken = STNodeFactory.createEmptyNode();
                constExprNode = STNodeFactory.createEmptyNode();
                break;
            default:
                Solution solution =
                        recover(peek(), ParserRuleContext.ENUM_MEMBER_INTERNAL_RHS, metadata, identifierNode);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseEnumMemberInternalRhs(metadata, identifierNode, solution.tokenKind);
        }

        return STNodeFactory.createEnumMemberNode(metadata, identifierNode, equalToken, constExprNode);
    }

    /**
     * Parse transaction statement.
     * <p>
     * <code>transaction-stmt := "transaction" block-stmt ;</code>
     *
     * @return Transaction statement node
     */
    private STNode parseTransactionStatement() {
        startContext(ParserRuleContext.TRANSACTION_STMT);
        STNode transactionKeyword = parseTransactionKeyword();
        STNode blockStmt = parseBlockNode();
        endContext();
        return STNodeFactory.createTransactionStatementNode(transactionKeyword, blockStmt);
    }

    /**
     * Parse transaction keyword.
     *
     * @return parsed node
     */
    private STNode parseTransactionKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.TRANSACTION_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.TRANSACTION_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse commit action.
     * <p>
     * <code>commit-action := "commit"</code>
     *
     * @return Commit action node
     */
    private STNode parseCommitAction() {
        STNode commitKeyword = parseCommitKeyword();
        return STNodeFactory.createCommitActionNode(commitKeyword);
    }

    /**
     * Parse commit keyword.
     *
     * @return parsed node
     */
    private STNode parseCommitKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.COMMIT_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.COMMIT_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse retry statement.
     * <p>
     * <code>
     * retry-stmt := "retry" retry-spec block-stmt
     * <br/>
     * retry-spec :=  [type-parameter] [ "(" arg-list ")" ]
     * </code>
     *
     * @return Retry statement node
     */
    private STNode parseRetryStatement() {
        startContext(ParserRuleContext.RETRY_STMT);
        STNode retryKeyword = parseRetryKeyword();
        STNode retryStmt = parseRetryKeywordRhs(retryKeyword);
        endContext();
        return retryStmt;
    }

    private STNode parseRetryKeywordRhs(STNode retryKeyword) {
        return parseRetryKeywordRhs(peek().kind, retryKeyword);
    }

    private STNode parseRetryKeywordRhs(SyntaxKind nextTokenKind, STNode retryKeyword) {
        switch (nextTokenKind) {
            case LT_TOKEN:
                STNode typeParam = parseTypeParameter();
                return parseRetryTypeParamRhs(retryKeyword, typeParam);
            case OPEN_PAREN_TOKEN:
            case OPEN_BRACE_TOKEN:
            case TRANSACTION_KEYWORD:
                typeParam = STNodeFactory.createEmptyNode();
                return parseRetryTypeParamRhs(nextTokenKind, retryKeyword, typeParam);
            default:
                Solution solution = recover(peek(), ParserRuleContext.RETRY_KEYWORD_RHS, retryKeyword);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseRetryKeywordRhs(solution.tokenKind, retryKeyword);
        }
    }

    private STNode parseRetryTypeParamRhs(STNode retryKeyword, STNode typeParam) {
        return parseRetryTypeParamRhs(peek().kind, retryKeyword, typeParam);
    }

    private STNode parseRetryTypeParamRhs(SyntaxKind nextTokenKind, STNode retryKeyword, STNode typeParam) {
        STNode args;
        switch (nextTokenKind) {
            case OPEN_PAREN_TOKEN:
                args = parseParenthesizedArgList();
                break;
            case OPEN_BRACE_TOKEN:
            case TRANSACTION_KEYWORD:
                args = STNodeFactory.createEmptyNode();
                break;
            default:
                Solution solution = recover(peek(), ParserRuleContext.RETRY_TYPE_PARAM_RHS, retryKeyword, typeParam);
                return parseRetryTypeParamRhs(solution.tokenKind, retryKeyword, typeParam);
        }

        STNode blockStmt = parseRetryBody();
        return STNodeFactory.createRetryStatementNode(retryKeyword, typeParam, args, blockStmt);
    }

    private STNode parseRetryBody() {
        return parseRetryBody(peek().kind);
    }

    private STNode parseRetryBody(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case OPEN_BRACE_TOKEN:
                return parseBlockNode();
            case TRANSACTION_KEYWORD:
                return parseTransactionStatement();
            default:
                Solution solution = recover(peek(), ParserRuleContext.RETRY_BODY);
                return parseRetryBody(solution.tokenKind);
        }
    }

    private STNode parseEnumMemberRhs() {
        return parseEnumMemberRhs(peek().kind);
    }

    private STNode parseEnumMemberRhs(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACE_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.ENUM_MEMBER_RHS);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseEnumMemberRhs(solution.tokenKind);
        }
    }

    /**
     * Parse retry keyword.
     *
     * @return parsed node
     */
    private STNode parseRetryKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.RETRY_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.RETRY_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse transaction statement.
     * <p>
     * <code>rollback-stmt := "rollback" [expression] ";"</code>
     *
     * @return Rollback statement node
     */
    private STNode parseRollbackStatement() {
        startContext(ParserRuleContext.ROLLBACK_STMT);
        STNode rollbackKeyword = parseRollbackKeyword();
        STNode expression;
        if (peek().kind == SyntaxKind.SEMICOLON_TOKEN) {
            expression = STNodeFactory.createEmptyNode();
        } else {
            expression = parseExpression();
        }

        STNode semicolon = parseSemicolon();
        endContext();
        return STNodeFactory.createRollbackStatementNode(rollbackKeyword, expression, semicolon);
    }

    /**
     * Parse rollback keyword.
     *
     * @return parsed node
     */
    private STNode parseRollbackKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ROLLBACK_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ROLLBACK_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse transactional expression.
     * <p>
     * <code>transactional-expr := "transactional"</code>
     *
     * @return Transactional expression node
     */
    private STNode parseTransactionalExpression() {
        STNode transactionalKeyword = parseTransactionalKeyword();
        return STNodeFactory.createTransactionalExpressionNode(transactionalKeyword);
    }

    /**
     * Parse transactional keyword.
     *
     * @return parsed node
     */
    private STNode parseTransactionalKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.TRANSACTIONAL_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ROLLBACK_KEYWORD);
            return sol.recoveredNode;
        }
    }

    private STNode parseDestructureAssignmentOrVarDecl(STNode annots) {
        startContext(ParserRuleContext.ASSIGNMENT_OR_VAR_DECL_STMT);
        STNode stmt;
        STNode listBindingPatternOrTupleType = parseListBindingPatternOrTupleType(true);
        if (listBindingPatternOrTupleType.kind == SyntaxKind.LIST_BINDING_PATTERN) {
            switchContext(ParserRuleContext.ASSIGNMENT_STMT);
            stmt = parseAssignmentStmtRhs(listBindingPatternOrTupleType);
        } else {
            switchContext(ParserRuleContext.VAR_DECL_STMT);
            STNode varName = parseBindingPattern();
            STNode typedBindingPattern =
                    STNodeFactory.createTypedBindingPatternNode(listBindingPatternOrTupleType, varName);
            stmt = parseVarDeclRhs(annots, STNodeFactory.createEmptyNode(), typedBindingPattern, false);
        }

        endContext();
        return stmt;
    }

    private STNode parseListBindingPatternOrTupleType(boolean isRoot) {
        startContext(ParserRuleContext.LIST_BP_OR_TUPLE_TYPE_DESC);
        STNode openBracket = parseOpenBracket();
        List<STNode> memberList = new ArrayList<>();

        
        STToken nextToken = peek();
        if (!isEndOfReceiveFields(nextToken.kind)) {
            
            STNode member;
            STNode memberEnd;
            nextToken = peek();
            while (!isEndOfReceiveFields(nextToken.kind)) {
                
                member = parseListBindingPatternOrTupleTypeAmbiguousMember();
                SyntaxKind currentNodeType = getParsingNodeType(member);

                switch (currentNodeType) {
                    case TUPLE_TYPE_DESC:
                        
                        
                        return parseAsTupleTypeDesc(openBracket, memberList, member);
                    case LIST_BINDING_PATTERN:
                        
                        
                        return parseAsListBindingPattern(openBracket, memberList, member);
                    case NONE:
                    default:
                        memberList.add(member);
                        break;
                }

                
                memberEnd = parseBracketedListMemberEnd();
                if (memberEnd == null) {
                    break;
                }
                memberList.add(memberEnd);
                nextToken = peek();
            }
        }

        
        STNode closeBracket = parseCloseBracket();
        STNode listBindingPatternOrTupleTypeDesc =
                parseListBindingPatternOrTupleType(openBracket, memberList, closeBracket, isRoot);
        endContext();
        return listBindingPatternOrTupleTypeDesc;
    }

    private STNode parseListBindingPatternOrTupleTypeAmbiguousMember() {
        return parseListBindingPatternOrTupleTypeAmbiguousMember(peek().kind);
    }

    /**
     * Parse a member of a list-binding-pattern or tuple-type-desc, when the parent is ambiguous.
     *
     * @param nextTokenKind Kind of the next token.
     * @return Parsed node
     */
    private STNode parseListBindingPatternOrTupleTypeAmbiguousMember(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case OPEN_BRACKET_TOKEN:
                
                return parseListBindingPatternOrTupleType(false);
            case IDENTIFIER_TOKEN:
                STNode identifier = parseQualifiedIdentifier(ParserRuleContext.VARIABLE_REF);
                nextTokenKind = peek().kind;
                if (isWildcardBP(identifier)) {
                    return STNodeFactory.createCaptureBindingPatternNode(identifier);
                }
                if (isTypeFollowingToken(nextTokenKind)) {
                    return parseComplexTypeDescriptor(identifier, ParserRuleContext.TYPE_DESC_IN_TUPLE, false);
                }
                if (nextTokenKind == SyntaxKind.OPEN_PAREN_TOKEN) {
                    
                    return parseListBindingPatternMember();
                }
                if (nextTokenKind == SyntaxKind.ELLIPSIS_TOKEN) {
                    STNode ellipsis = parseEllipsis();
                    return STNodeFactory.createRestDescriptorNode(identifier, ellipsis);
                }

                
                return identifier;
            case OPEN_BRACE_TOKEN:
                
                return parseListBindingPatternMember();
            case ERROR_KEYWORD:
                if (getNextNextToken(nextTokenKind).kind == SyntaxKind.OPEN_PAREN_TOKEN) {
                    
                    return parseListBindingPatternMember();
                }

                
                return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TUPLE);
            case ELLIPSIS_TOKEN:
                return parseListBindingPatternMember();
            default:
                if (isTypeStartingToken(nextTokenKind)) {
                    return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TUPLE);
                }

                Solution solution = recover(peek(), ParserRuleContext.LIST_BP_OR_TUPLE_TYPE_MEMBER);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseListBindingPatternOrTupleTypeAmbiguousMember(solution.tokenKind);
        }
    }

    private STNode parseAsTupleTypeDesc(STNode openBracket, List<STNode> memberList, STNode member) {
        memberList = getTypeDescList(memberList);
        switchContext(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
        startContext(ParserRuleContext.TYPE_DESC_IN_TUPLE);
        STNode tupleTypeMembers = parseTupleTypeMembers(member, memberList);
        STNode closeBracket = parseCloseBracket();
        endContext();
        endContext();
        return STNodeFactory.createTupleTypeDescriptorNode(openBracket, tupleTypeMembers, closeBracket);
    }

    private STNode parseAsListBindingPattern(STNode openBracket, List<STNode> memberList, STNode member) {
        memberList = getBindingPattern(memberList);
        memberList.add(member);
        switchContext(ParserRuleContext.LIST_BINDING_PATTERN);
        STNode listBindingPattern = parseListBindingPattern(openBracket, member, memberList);
        endContext();
        return listBindingPattern;
    }

    private STNode parseAsListBindingPattern(STNode openBracket, List<STNode> memberList) {
        memberList = getBindingPattern(memberList);
        switchContext(ParserRuleContext.LIST_BINDING_PATTERN);
        STNode listBindingPattern = parseListBindingPattern(openBracket, memberList);
        endContext();
        return listBindingPattern;
    }

    private List<STNode> getTypeDescList(List<STNode> ambibuousList) {
        List<STNode> typeDescList = new ArrayList<STNode>();
        for (STNode item : ambibuousList) {
            
            
            
            if (item.kind != SyntaxKind.LIST_BP_OR_TUPLE_TYPE_DESC) {
                typeDescList.add(item);
                continue;
            }

            STAmbiguousListNode innerList = (STAmbiguousListNode) item;
            STNode memberTypeDescList = STNodeFactory.createNodeList(innerList.members);
            STNode typeDesc = STNodeFactory.createTupleTypeDescriptorNode(innerList.openBracket, memberTypeDescList,
                    innerList.closeBracket);
            typeDescList.add(typeDesc);
        }

        return typeDescList;
    }

    private List<STNode> getBindingPattern(List<STNode> ambibuousList) {
        List<STNode> typeDescList = new ArrayList<STNode>();
        for (STNode item : ambibuousList) {
            
            
            
            switch (item.kind) {
                case SIMPLE_NAME_REFERENCE:
                    typeDescList.add(STNodeFactory.createCaptureBindingPatternNode(item));
                    break;
                case LIST_BP_OR_TUPLE_TYPE_DESC:
                    STAmbiguousListNode innerList = (STAmbiguousListNode) item;
                    STNode memberBindingPatterns = STNodeFactory.createNodeList(innerList.members);
                    STNode restBindingPattern = STNodeFactory.createEmptyNode();
                    STNode typeDesc = STNodeFactory.createListBindingPatternNode(innerList.openBracket,
                            memberBindingPatterns, restBindingPattern, innerList.closeBracket);
                    typeDescList.add(typeDesc);
                    break;
                default:
                    typeDescList.add(item);
                    break;
            }
        }

        return typeDescList;
    }

    private boolean isTypeFollowingToken(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case OPEN_BRACKET_TOKEN: 
            case PIPE_TOKEN: 
            case BITWISE_AND_TOKEN: 
            case QUESTION_MARK_TOKEN: 
                return true;
            default:
                return false;
        }
    }

    private SyntaxKind getParsingNodeType(STNode memberNode) {
        if (memberNode.kind.compareTo(SyntaxKind.TYPE_DESC) >= 0 &&
                memberNode.kind.compareTo(SyntaxKind.SINGLETON_TYPE_DESC) <= 0) {
            return SyntaxKind.TUPLE_TYPE_DESC;
        }

        switch (memberNode.kind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case ASTERISK_TOKEN:
                return SyntaxKind.ARRAY_TYPE_DESC;
            case CAPTURE_BINDING_PATTERN:
            case LIST_BINDING_PATTERN:
            case REST_BINDING_PATTERN:
                return SyntaxKind.LIST_BINDING_PATTERN;
            case QUALIFIED_NAME_REFERENCE: 
            case REST_TYPE:
                return SyntaxKind.TUPLE_TYPE_DESC;
            case SIMPLE_NAME_REFERENCE: 
            case LIST_BP_OR_TUPLE_TYPE_DESC: 
            default:
                return SyntaxKind.NONE;
        }
    }

    private STNode parseListBindingPatternOrTupleType(STNode openBracket, List<STNode> members, STNode closeBracket,
                                                      boolean isRoot) {
        if (!isRoot) {
            
            
            
            return new STAmbiguousListNode(SyntaxKind.LIST_BP_OR_TUPLE_TYPE_DESC, openBracket, members, closeBracket);
        }

        switch (peek().kind) {
            case EQUAL_TOKEN:
                STNode memberBindingPatterns = STNodeFactory.createNodeList(getBindingPattern(members));
                STNode restBindingPattern = STNodeFactory.createEmptyNode();
                return STNodeFactory.createListBindingPatternNode(openBracket, memberBindingPatterns,
                        restBindingPattern, closeBracket);
            default:
                if (members.isEmpty()) {
                    this.errorHandler.reportMissingTokenError("missing member");
                }
                STNode memberTypeDescs = STNodeFactory.createNodeList(getTypeDescList(members));
                return STNodeFactory.createTupleTypeDescriptorNode(openBracket, memberTypeDescs, closeBracket);
        }
    }

    private boolean isWildcardBP(STNode node) {
        if (node.kind != SyntaxKind.SIMPLE_NAME_REFERENCE) {
            return false;
        }

        STToken nameToken = (STToken) ((STSimpleNameReferenceNode) node).name;
        return "_".equals(nameToken.text());
    }

    /**
     * Parse service-constructor-expr.
     *
     * service-constructor-expr := [annots] service service-body-block
     * service-body-block := { service-method-defn* }
     * service-method-defn :=
     *    metadata
     *    [resource]
     *    function identifier function-signature method-defn-body
     *
     * @param annots Annots
     * @return Parsed node
     */
    private STNode parseServiceConstructorExpression(STNode annots) {
        startContext(ParserRuleContext.SERVICE_CONSTRUCTOR_EXPRESSION);
        STNode serviceKeyword = parseServiceKeyword();
        STNode serviceBody = parseServiceBody();
        endContext();
        return STNodeFactory.createServiceConstructorExpressionNode(annots,
                serviceKeyword,
                serviceBody);
    }

    /**
     * Parse base16 literal.
     * <p>
     * <code>
     * byte-array-literal := Base16Literal | Base64Literal
     * <br/>
     * Base16Literal := base16 WS ` HexGroup* WS `
     * <br/>
     * Base64Literal := base64 WS ` Base64Group* [PaddedBase64Group] WS `
     * </code>
     *
     * @param kind byte array literal kind
     * @return parsed node
     */
    private STNode parseByteArrayLiteral(SyntaxKind kind) {
        STNode type;

        if (kind == SyntaxKind.BASE16_KEYWORD) {
            type = parseBase16Keyword();
        } else {
            type = parseBase64Keyword();
        }

        STNode startingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_START);
        STNode content = parseByteArrayContent(kind);
        STNode endingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_END);
        return STNodeFactory.createByteArrayLiteralNode(type, startingBackTick, content, endingBackTick);
    }

    /**
     * Parse <code>base16</code> keyword.
     *
     * @return base16 keyword node
     */
    private STNode parseBase16Keyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.BASE16_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.BASE16_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse <code>base64</code> keyword.
     *
     * @return base64 keyword node
     */
    private STNode parseBase64Keyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.BASE64_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.BASE64_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Validate and parse byte array literal content.
     * An error is reported, if the content is invalid.
     *
     * @param kind byte array literal kind
     * @return parsed node
     */
    private STNode parseByteArrayContent(SyntaxKind kind) {
        STNode content = STNodeFactory.createEmptyNode();
        STToken nextToken = peek();

        List<STNode> items = new ArrayList<>();
        while (!isEndOfBacktickContent(nextToken.kind)) {
            content = parseTemplateItem();
            items.add(content);
            nextToken = peek();
        }

        if (items.size() > 1) {
            this.errorHandler.reportInvalidNode(null, "invalid content within backticks");
        } else if (items.size() == 1 && content.kind != SyntaxKind.TEMPLATE_STRING) {
            this.errorHandler.reportInvalidNode(null, "invalid content within backticks");
        } else if (items.size() == 1) {
            if (kind == SyntaxKind.BASE16_KEYWORD &&
                    !BallerinaLexer.isValidBase16LiteralContent(content.toString())) {
                this.errorHandler.reportInvalidNode(null, "invalid content within backticks");
            } else if (kind == SyntaxKind.BASE64_KEYWORD &&
                    !BallerinaLexer.isValidBase64LiteralContent(content.toString())) {
                this.errorHandler.reportInvalidNode(null, "invalid content within backticks");
            }
        }

        return content;
    }

    /**
     * Parse xml filter expression.
     * <p>
     * <code>xml-filter-expr := expression .< xml-name-pattern ></code>
     *
     * @param lhsExpr Preceding expression of .< token
     * @return Parsed node
     */
    private STNode parseXMLFilterExpression(STNode lhsExpr) {
        STNode xmlNamePatternChain = parseXMLFilterExpressionRhs();
        return STNodeFactory.createXMLFilterExpressionNode(lhsExpr, xmlNamePatternChain);
    }

    /**
     * Parse xml filter expression rhs.
     * <p>
     * <code>filer-expression-rhs := .< xml-name-pattern ></code>
     *
     * @return Parsed node
     */
    private STNode parseXMLFilterExpressionRhs() {
        STNode dotLTToken = parseDotLTToken();
        return parseXMLNamePatternChain(dotLTToken);
    }

    /**
     * Parse xml name pattern chain.
     * <p>
     * <code>
     * xml-name-pattern-chain := filer-expression-rhs | xml-element-children-step | xml-element-descendants-step
     * <br/>
     * filer-expression-rhs := .< xml-name-pattern >
     * <br/>
     * xml-element-children-step := /< xml-name-pattern >
     * <br/>
     * xml-element-descendants-step := /**\/<xml-name-pattern >
     * </code>
     *
     * @param startToken Preceding token of xml name pattern
     * @return Parsed node
     */
    private STNode parseXMLNamePatternChain(STNode startToken) {
        STNode xmlNamePattern = parseXMLNamePattern();
        STNode gtToken = parseGTToken();
        return STNodeFactory.createXMLNamePatternChainingNode(startToken, xmlNamePattern, gtToken);
    }

    /**
     * Parse <code> .< </code> token.
     *
     * @return Parsed node
     */
    private STNode parseDotLTToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.DOT_LT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.DOT_LT_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse xml name pattern.
     * <p>
     * <code>xml-name-pattern := xml-atomic-name-pattern [| xml-atomic-name-pattern]*</code>
     *
     * @return Parsed node
     */
    private STNode parseXMLNamePattern() {
        List<STNode> xmlAtomicNamePatternList = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfXMLNamePattern(nextToken.kind)) {
            this.errorHandler.reportMissingTokenError("missing xml atomic name pattern");
            return STNodeFactory.createNodeList(xmlAtomicNamePatternList);
        }

        
        STNode xmlAtomicNamePattern = parseXMLAtomicNamePattern();
        xmlAtomicNamePatternList.add(xmlAtomicNamePattern);

        
        nextToken = peek();
        STNode leadingPipe;
        while (!isEndOfXMLNamePattern(nextToken.kind)) {
            leadingPipe = parsePipeToken();
            xmlAtomicNamePatternList.add(leadingPipe);
            xmlAtomicNamePattern = parseXMLAtomicNamePattern();
            xmlAtomicNamePatternList.add(xmlAtomicNamePattern);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(xmlAtomicNamePatternList);
    }

    private boolean isEndOfXMLNamePattern(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case IDENTIFIER_TOKEN:
            case ASTERISK_TOKEN:
            case COLON_TOKEN:
                return false;
            case GT_TOKEN:
            case EOF_TOKEN:
            case AT_TOKEN:
            case DOCUMENTATION_LINE:
            case CLOSE_BRACE_TOKEN:
            case SEMICOLON_TOKEN:
            case PUBLIC_KEYWORD:
            case PRIVATE_KEYWORD:
            case FUNCTION_KEYWORD:
            case RETURNS_KEYWORD:
            case SERVICE_KEYWORD:
            case TYPE_KEYWORD:
            case LISTENER_KEYWORD:
            case CONST_KEYWORD:
            case FINAL_KEYWORD:
            case RESOURCE_KEYWORD:
                return true;
            default:
                return isSimpleType(tokenKind);
        }
    }

    /**
     * Parse xml atomic name pattern.
     * <p>
     * <code>
     * xml-atomic-name-pattern :=
     *   *
     *   | identifier
     *   | xml-namespace-prefix : identifier
     *   | xml-namespace-prefix : *
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseXMLAtomicNamePattern() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ASTERISK_TOKEN) {
            return consume();
        } else if (token.kind == SyntaxKind.IDENTIFIER_TOKEN) {
            STNode identifier = consume();
            return parseXMLAtomicNameIdentifier(identifier);
        }
        return STNodeFactory.createEmptyNode();
    }

    private STNode parseXMLAtomicNameIdentifier(STNode identifier) {
        STToken token = peek();
        if (token.kind == SyntaxKind.COLON_TOKEN) {
            STNode colon = consume();
            STToken nextToken = peek();
            if (nextToken.kind == SyntaxKind.IDENTIFIER_TOKEN || nextToken.kind == SyntaxKind.ASTERISK_TOKEN) {
                STToken endToken = consume();
                return STNodeFactory.createXMLAtomicNamePatternNode(identifier, colon, endToken);
            }
        }
        return STNodeFactory.createSimpleNameReferenceNode(identifier);
    }

    /**
     * Parse xml step expression.
     * <p>
     * <code>xml-step-expr := expression xml-step-start xml-step-extend*</code>
     *
     * @param lhsExpr Preceding expression of /*, /<, or /**\/< token
     * @return Parsed node
     */
    private STNode parseXMLStepExpression(STNode lhsExpr) {
        STNode xmlStepStart = parseXMLStepStart();
        STNode xmlStepExtendList = parseXMLStepExtendList();
        return STNodeFactory.createXMLStepExpressionNode(lhsExpr, xmlStepStart, xmlStepExtendList);
    }

    /**
     * Parse xml filter expression rhs.
     * <p>
     * <code>
     *  xml-step-start :=
     *      xml-all-children-step
     *      | xml-element-children-step
     *      | xml-element-descendants-step
     * <br/>
     * xml-all-children-step := /*
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseXMLStepStart() {
        STToken token = peek();
        STNode startToken;

        switch (token.kind) {
            case SLASH_ASTERISK_TOKEN:
                return consume();
            case DOUBLE_SLASH_DOUBLE_ASTERISK_LT_TOKEN:
                startToken = parseDoubleSlashDoubleAsteriskLTToken();
                break;
            case SLASH_LT_TOKEN:
            default:
                startToken = parseSlashLTToken();
                break;
        }
        return parseXMLNamePatternChain(startToken);
    }

    /**
     * Parse <code> /< </code> token.
     *
     * @return Parsed node
     */
    private STNode parseSlashLTToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.SLASH_LT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.SLASH_LT_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse <code> /< </code> token.
     *
     * @return Parsed node
     */
    private STNode parseDoubleSlashDoubleAsteriskLTToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.DOUBLE_SLASH_DOUBLE_ASTERISK_LT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.DOUBLE_SLASH_DOUBLE_ASTERISK_LT_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse xml step extend list.
     * <p>
     * <code>xml-step-extend-list := xml-step-extend*</code>
     *
     * @return Parsed node
     */
    private STNode parseXMLStepExtendList() {
        List<STNode> xmlStepExtendList = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfXMLStepExtend(nextToken.kind)) {
            return STNodeFactory.createNodeList(xmlStepExtendList);
        }

        nextToken = peek();
        STNode xmlStepExtend;
        while (!isEndOfXMLNamePattern(nextToken.kind)) {
            xmlStepExtend = parseXMLStepExtend();
            xmlStepExtendList.add(xmlStepExtend);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(xmlStepExtendList);
    }

    private boolean isEndOfXMLStepExtend(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case DOT_LT_TOKEN:
            case OPEN_BRACKET_TOKEN:
            case DOT_TOKEN:
                return false;
            default:
                return true;
        }
    }

    /**
     * Parse xml step extend list.
     * <p>
     * <code>
     * xml-step-extend :=
     *    .< xml-name-pattern >
     *    | [ expression ]
     *    | . method-name ( arg-list )
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseXMLStepExtend() {
        STToken token = peek();
        
        
        switch (token.kind) {
            case DOT_LT_TOKEN:
                return parseXMLFilterExpressionRhs();
            case DOT_TOKEN:
            case OPEN_BRACKET_TOKEN:
            default:
                STNode lhsExpr = STNodeFactory.createEmptyNode();
                return parseExpressionRhs(token.kind, OperatorPrecedence.ACTION, lhsExpr, true, false);
        }
    }
}

Line commented on: STNode xmlStepExtendList = parseXMLStepExtendList();

Revised code:
private boolean isServiceDeclStart(ParserRuleContext currentContext, int lookahead) {
        
        switch (peek(lookahead + 1).kind) {
            case IDENTIFIER_TOKEN:
                SyntaxKind tokenAfterIdentifier = peek(lookahead + 2).kind;
                switch (tokenAfterIdentifier) {
                    case ON_KEYWORD: 
                    case OPEN_BRACE_TOKEN: 
                        return true;
                    case EQUAL_TOKEN: 
                    case SEMICOLON_TOKEN: 
                    case QUESTION_MARK_TOKEN: 
                        return false;
                    default:
                        
                        return false;
                }
            case ON_KEYWORD:
                
                
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse listener declaration, given the qualifier.
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the listener declaration
     * @return Parsed node
     */
    private STNode parseListenerDeclaration(STNode metadata, STNode qualifier) {
        startContext(ParserRuleContext.LISTENER_DECL);
        STNode listenerKeyword = parseListenerKeyword();
        STNode typeDesc = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_BEFORE_IDENTIFIER);
        STNode variableName = parseVariableName();
        STNode equalsToken = parseAssignOp();
        STNode initializer = parseExpression();
        STNode semicolonToken = parseSemicolon();
        endContext();
        return STNodeFactory.createListenerDeclarationNode(metadata, qualifier, listenerKeyword, typeDesc, variableName,
                equalsToken, initializer, semicolonToken);
    }

    /**
     * Parse listener keyword.
     *
     * @return Parsed node
     */
    private STNode parseListenerKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.LISTENER_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.LISTENER_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse constant declaration, given the qualifier.
     * <p>
     * <code>module-const-decl := metadata [public] const [type-descriptor] identifier = const-expr ;</code>
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the listener declaration
     * @return Parsed node
     */
    private STNode parseConstantDeclaration(STNode metadata, STNode qualifier) {
        startContext(ParserRuleContext.CONSTANT_DECL);
        STNode constKeyword = parseConstantKeyword();
        STNode constDecl = parseConstDecl(metadata, qualifier, constKeyword);
        endContext();
        return constDecl;
    }

    /**
     * Parse the components that follows after the const keyword of a constant declaration.
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the constant decl
     * @param constKeyword Const keyword
     * @return Parsed node
     */
    private STNode parseConstDecl(STNode metadata, STNode qualifier, STNode constKeyword) {
        STToken nextToken = peek();
        return parseConstDeclFromType(nextToken.kind, metadata, qualifier, constKeyword);
    }

    private STNode parseConstDeclFromType(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
                                          STNode constKeyword) {
        switch (nextTokenKind) {
            case ANNOTATION_KEYWORD:
                switchContext(ParserRuleContext.ANNOTATION_DECL);
                return parseAnnotationDeclaration(metadata, qualifier, constKeyword);
            case IDENTIFIER_TOKEN:
                return parseConstantDeclWithOptionalType(metadata, qualifier, constKeyword);
            default:
                if (isTypeStartingToken(nextTokenKind)) {
                    break;
                }
                STToken token = peek();
                Solution solution =
                        recover(token, ParserRuleContext.CONST_DECL_TYPE, metadata, qualifier, constKeyword);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseConstDeclFromType(solution.tokenKind, metadata, qualifier, constKeyword);
        }

        STNode typeDesc = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_BEFORE_IDENTIFIER);
        STNode variableName = parseVariableName();
        STNode equalsToken = parseAssignOp();
        STNode initializer = parseExpression();
        STNode semicolonToken = parseSemicolon();
        return STNodeFactory.createConstantDeclarationNode(metadata, qualifier, constKeyword, typeDesc, variableName,
                equalsToken, initializer, semicolonToken);
    }

    private STNode parseConstantDeclWithOptionalType(STNode metadata, STNode qualifier, STNode constKeyword) {
        STNode varNameOrTypeName = parseStatementStartIdentifier();
        STNode constDecl = parseConstantDeclRhs(metadata, qualifier, constKeyword, varNameOrTypeName);
        return constDecl;
    }

    /**
     * Parse the component that follows the first identifier in a const decl. The identifier
     * can be either the type-name (a user defined type) or the var-name there the type-name
     * is not present.
     *
     * @param qualifier Qualifier that precedes the constant decl
     * @param constKeyword Const keyword
     * @param typeOrVarName Identifier that follows the const-keywoord
     * @return Parsed node
     */
    private STNode parseConstantDeclRhs(STNode metadata, STNode qualifier, STNode constKeyword, STNode typeOrVarName) {
        STToken token = peek();
        return parseConstantDeclRhs(token.kind, metadata, qualifier, constKeyword, typeOrVarName);
    }

    private STNode parseConstantDeclRhs(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
                                        STNode constKeyword, STNode typeOrVarName) {
        STNode type;
        STNode variableName;
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN:
                type = typeOrVarName;
                variableName = parseVariableName();
                break;
            case EQUAL_TOKEN:
                variableName = ((STSimpleNameReferenceNode) typeOrVarName).name; 
                type = STNodeFactory.createEmptyNode();
                break;
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.CONST_DECL_RHS, metadata, qualifier, constKeyword,
                        typeOrVarName);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseConstantDeclRhs(solution.tokenKind, metadata, qualifier, constKeyword, typeOrVarName);
        }

        STNode equalsToken = parseAssignOp();
        STNode initializer = parseExpression();
        STNode semicolonToken = parseSemicolon();
        return STNodeFactory.createConstantDeclarationNode(metadata, qualifier, constKeyword, type, variableName,
                equalsToken, initializer, semicolonToken);
    }

    /**
     * Parse const keyword.
     *
     * @return Parsed node
     */
    private STNode parseConstantKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.CONST_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.CONST_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse nil type descriptor.
     * <p>
     * <code>nil-type-descriptor :=  ( ) </code>
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseNilTypeDescriptor() {
        startContext(ParserRuleContext.NIL_TYPE_DESCRIPTOR);
        STNode openParenthesisToken = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
        STNode closeParenthesisToken = parseCloseParenthesis();
        endContext();

        return STNodeFactory.createNilTypeDescriptorNode(openParenthesisToken, closeParenthesisToken);
    }

    /**
     * Parse typeof expression.
     * <p>
     * <code>
     * typeof-expr := typeof expression
     * </code>
     *
     * @param isRhsExpr
     * @return Typeof expression node
     */
    private STNode parseTypeofExpression(boolean isRhsExpr) {
        STNode typeofKeyword = parseTypeofKeyword();

        
        
        STNode expr = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false);
        return STNodeFactory.createTypeofExpressionNode(typeofKeyword, expr);
    }

    /**
     * Parse typeof-keyword.
     *
     * @return Typeof-keyword node
     */
    private STNode parseTypeofKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.TYPEOF_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.TYPEOF_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse optional type descriptor.
     * <p>
     * <code>optional-type-descriptor := type-descriptor ? </code>
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseOptionalTypeDescriptor(STNode typeDescriptorNode) {
        startContext(ParserRuleContext.OPTIONAL_TYPE_DESCRIPTOR);
        STNode questionMarkToken = parseQuestionMark();
        endContext();

        return STNodeFactory.createOptionalTypeDescriptorNode(typeDescriptorNode, questionMarkToken);
    }

    /**
     * Parse unary expression.
     * <p>
     * <code>
     * unary-expr := + expression | - expression | ~ expression | ! expression
     * </code>
     *
     * @param isRhsExpr
     * @return Unary expression node
     */
    private STNode parseUnaryExpression(boolean isRhsExpr) {
        STNode unaryOperator = parseUnaryOperator();

        
        
        STNode expr = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false);
        return STNodeFactory.createUnaryExpressionNode(unaryOperator, expr);
    }

    /**
     * Parse unary operator.
     * <code>UnaryOperator := + | - | ~ | !</code>
     *
     * @return Parsed node
     */
    private STNode parseUnaryOperator() {
        STToken token = peek();
        if (isUnaryOperator(token.kind)) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.UNARY_OPERATOR);
            return sol.recoveredNode;
        }
    }

    /**
     * Check whether the given token kind is a unary operator.
     *
     * @param kind STToken kind
     * @return <code>true</code> if the token kind refers to a unary operator. <code>false</code> otherwise
     */
    private boolean isUnaryOperator(SyntaxKind kind) {
        switch (kind) {
            case PLUS_TOKEN:
            case MINUS_TOKEN:
            case NEGATION_TOKEN:
            case EXCLAMATION_MARK_TOKEN:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse array type descriptor.
     * <p>
     * <code>
     * array-type-descriptor := member-type-descriptor [ [ array-length ] ]
     * member-type-descriptor := type-descriptor
     * array-length :=
     *    int-literal
     *    | constant-reference-expr
     *    | inferred-array-length
     * inferred-array-length := *
     * </code>
     * </p>
     *
     * @param memberTypeDesc
     *
     * @return Parsed Node
     */
    private STNode parseArrayTypeDescriptor(STNode memberTypeDesc) {
        startContext(ParserRuleContext.ARRAY_TYPE_DESCRIPTOR);
        STNode openBracketToken = parseOpenBracket();
        STNode arrayLengthNode = parseArrayLength();
        STNode closeBracketToken = parseCloseBracket();
        endContext();
        return STNodeFactory.createArrayTypeDescriptorNode(memberTypeDesc, openBracketToken, arrayLengthNode,
                closeBracketToken);
    }

    /**
     * Parse array length.
     * <p>
     * <code>
     *     array-length :=
     *    int-literal
     *    | constant-reference-expr
     *    | inferred-array-length
     * constant-reference-expr := variable-reference-expr
     * </code>
     * </p>
     *
     * @return Parsed array length
     */
    private STNode parseArrayLength() {
        STToken token = peek();
        switch (token.kind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case ASTERISK_TOKEN:
                return parseBasicLiteral();
            case CLOSE_BRACKET_TOKEN:
                return STNodeFactory.createEmptyNode();
            
            case IDENTIFIER_TOKEN:
                return parseQualifiedIdentifier(ParserRuleContext.ARRAY_LENGTH);
            default:
                Solution sol = recover(token, ParserRuleContext.ARRAY_LENGTH);
                return sol.recoveredNode;
        }
    }

    /**
     * Parse annotations.
     * <p>
     * <i>Note: In the ballerina spec ({@link https:
     * annotations-list is specified as one-or-more annotations. And the usage is marked as
     * optional annotations-list. However, for the consistency of the tree, here we make the
     * annotation-list as zero-or-more annotations, and the usage is not-optional.</i>
     * <p>
     * <code>annots := annotation*</code>
     *
     * @return Parsed node
     */
    private STNode parseAnnotations() {
        STToken nextToken = peek();
        return parseAnnotations(nextToken.kind);
    }

    private STNode parseAnnotations(SyntaxKind nextTokenKind) {
        startContext(ParserRuleContext.ANNOTATIONS);
        List<STNode> annotList = new ArrayList<>();
        while (nextTokenKind == SyntaxKind.AT_TOKEN) {
            annotList.add(parseAnnotation());
            nextTokenKind = peek().kind;
        }

        endContext();
        return STNodeFactory.createNodeList(annotList);
    }

    /**
     * Parse annotation attachment.
     * <p>
     * <code>annotation := @ annot-tag-reference annot-value</code>
     *
     * @return Parsed node
     */
    private STNode parseAnnotation() {
        STNode atToken = parseAtToken();
        STNode annotReference;
        if (peek().kind != SyntaxKind.IDENTIFIER_TOKEN) {
            annotReference = STNodeFactory.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN);
        } else {
            annotReference = parseQualifiedIdentifier(ParserRuleContext.ANNOT_REFERENCE);
        }

        STNode annotValue;
        if (peek().kind == SyntaxKind.OPEN_BRACE_TOKEN) {
            annotValue = parseMappingConstructorExpr();
        } else {
            annotValue = STNodeFactory.createEmptyNode();
        }
        return STNodeFactory.createAnnotationNode(atToken, annotReference, annotValue);
    }

    /**
     * Parse '@' token.
     *
     * @return Parsed node
     */
    private STNode parseAtToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.AT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.AT);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse metadata. Meta data consist of optional doc string and
     * an annotations list.
     * <p>
     * <code>metadata := [DocumentationString] annots</code>
     *
     * @return Parse node
     */
    private STNode parseMetaData(SyntaxKind nextTokenKind) {
        STNode docString;
        STNode annotations;
        switch (nextTokenKind) {
            case DOCUMENTATION_LINE:
                docString = parseDocumentationString();
                annotations = parseAnnotations();
                break;
            case AT_TOKEN:
                docString = STNodeFactory.createEmptyNode();
                annotations = parseAnnotations(nextTokenKind);
                break;
            default:
                return createEmptyMetadata();
        }

        return STNodeFactory.createMetadataNode(docString, annotations);
    }

    /**
     * Create empty metadata node.
     *
     * @return A metadata node with no doc string and no annotations
     */
    private STNode createEmptyMetadata() {
        return STNodeFactory.createMetadataNode(STNodeFactory.createEmptyNode(),
                STNodeFactory.createNodeList(new ArrayList<>()));
    }

    /**
     * Parse is expression.
     * <code>
     * is-expr := expression is type-descriptor
     * </code>
     *
     * @param lhsExpr Preceding expression of the is expression
     * @return Is expression node
     */
    private STNode parseTypeTestExpression(STNode lhsExpr) {
        STNode isKeyword = parseIsKeyword();
        STNode typeDescriptor = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_EXPRESSION);
        return STNodeFactory.createTypeTestExpressionNode(lhsExpr, isKeyword, typeDescriptor);
    }

    /**
     * Parse is-keyword.
     *
     * @return Is-keyword node
     */
    private STNode parseIsKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.IS_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.IS_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse local type definition statement statement.
     * <code>ocal-type-defn-stmt := [annots] type identifier type-descriptor ;</code>
     *
     * @return local type definition statement statement
     */
    private STNode parseLocalTypeDefinitionStatement(STNode annots) {
        startContext(ParserRuleContext.LOCAL_TYPE_DEFINITION_STMT);
        STNode typeKeyword = parseTypeKeyword();
        STNode typeName = parseTypeName();
        STNode typeDescriptor = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TYPE_DEF);
        STNode semicolon = parseSemicolon();
        endContext();
        return STNodeFactory.createLocalTypeDefinitionStatementNode(annots, typeKeyword, typeName, typeDescriptor,
                semicolon);
    }

    /**
     * Pass statements that starts with an identifier.
     *
     * @param tokenKind Next token kind
     * @return Parsed node
     */
    private STNode parseStatementStartsWithIdentifier(STNode annots) {
        startContext(ParserRuleContext.AMBIGUOUS_STMT);
        STNode identifier = parseStatementStartIdentifier();
        return parseStatementStartIdentifierRhs(annots, identifier);
    }

    private STNode parseStatementStartIdentifierRhs(STNode annots, STNode identifier) {
        return parseStatementStartIdentifierRhs(peek().kind, annots, identifier);
    }

    private STNode parseStatementStartIdentifierRhs(SyntaxKind nextTokenKind, STNode annots, STNode identifier) {
        switch (nextTokenKind) {
            case OPEN_BRACKET_TOKEN:
                STNode tbpOrMemberAccess =
                        parseTypedBindingPatternOrMemberAccess(identifier, false, ParserRuleContext.AMBIGUOUS_STMT);
                if (tbpOrMemberAccess.kind == SyntaxKind.INDEXED_EXPRESSION) {
                    STNode expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, tbpOrMemberAccess, false, true);
                    return parseStatementStartWithExpr(expr);
                }

                STNode finalKeyword = STNodeFactory.createEmptyNode();
                return parseVarDeclRhs(annots, finalKeyword, tbpOrMemberAccess, false);
            case IDENTIFIER_TOKEN:
            case QUESTION_MARK_TOKEN:
                
                return parseTypeDescStartsWithIdentifier(identifier, annots);
            case EQUAL_TOKEN:
            case SEMICOLON_TOKEN:
                
                return parseStatementStartWithExpr(nextTokenKind, identifier);
            case PIPE_TOKEN:
            case BITWISE_AND_TOKEN:
                STToken nextNextToken = peek(2);
                if (nextNextToken.kind != SyntaxKind.EQUAL_TOKEN) {
                    return parseTypeDescStartsWithIdentifier(identifier, annots);
                }
                
            default:
                
                if (isCompoundBinaryOperator(nextTokenKind)) {
                    return parseCompoundAssignmentStmtRhs(identifier);
                }
                
                
                if (isValidExprRhsStart(nextTokenKind)) {
                    STNode expression =
                            parseExpressionRhs(nextTokenKind, DEFAULT_OP_PRECEDENCE, identifier, false, true);
                    return parseStatementStartWithExpr(expression);
                }

                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.STMT_START_IDENTIFIER_RHS, annots, identifier);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseStatementStartIdentifierRhs(solution.tokenKind, annots, identifier);
        }
    }

    private STNode parseTypeDescStartsWithIdentifier(STNode typeDesc, STNode annots) {
        switchContext(ParserRuleContext.VAR_DECL_STMT);

        
        
        startContext(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
        typeDesc = parseComplexTypeDescriptor(typeDesc, ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN, false);
        endContext();

        STNode finalKeyword = STNodeFactory.createEmptyNode();
        STNode typeBindingPattern = parseTypedBindingPatternTypeRhs(typeDesc, ParserRuleContext.VAR_DECL_STMT);
        return parseVarDeclRhs(annots, finalKeyword, typeBindingPattern, false);
    }

    /**
     * Parse statement which is only consists of an action or expression.
     *
     * @param annots Annotations
     * @param nextTokenKind Next token kind
     * @return Parsed node
     */
    private STNode parseExpressionStament(SyntaxKind nextTokenKind, STNode annots) {
        startContext(ParserRuleContext.EXPRESSION_STATEMENT);
        STNode expression = parseActionOrExpressionInLhs(nextTokenKind, annots);
        return getExpressionAsStatement(expression);
    }

    private STNode parseStamentStartWithExpr(SyntaxKind nextTokenKind, STNode annots) {
        startContext(ParserRuleContext.EXPRESSION_STATEMENT);
        STNode expression = parseActionOrExpressionInLhs(nextTokenKind, annots);
        return parseStatementStartWithExpr(expression);
    }

    /**
     * Parse statements that starts with an expression.
     *
     * @return Parsed node
     */
    private STNode parseStatementStartWithExpr(STNode expression) {
        STToken nextToken = peek();
        return parseStatementStartWithExpr(nextToken.kind, expression);
    }

    /**
     * Parse the component followed by the expression, at the beginning of a statement.
     *
     * @param nextTokenKind Kind of the next token
     * @return Parsed node
     */
    private STNode parseStatementStartWithExpr(SyntaxKind nextTokenKind, STNode expression) {
        switch (nextTokenKind) {
            case EQUAL_TOKEN:
                switchContext(ParserRuleContext.ASSIGNMENT_STMT);
                return parseAssignmentStmtRhs(expression);
            case SEMICOLON_TOKEN:
                return getExpressionAsStatement(expression);
            case IDENTIFIER_TOKEN:
            default:
                
                if (isCompoundBinaryOperator(nextTokenKind)) {
                    return parseCompoundAssignmentStmtRhs(expression);
                }

                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.STMT_START_WITH_EXPR_RHS, expression);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseStatementStartWithExpr(solution.tokenKind, expression);
        }
    }

    private STNode getArrayLength(STNodeList exprs) {
        if (exprs.isEmpty()) {
            return STNodeFactory.createEmptyNode();
        }

        STNode lengthExpr = exprs.get(0);
        switch (lengthExpr.kind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case ASTERISK_TOKEN:
            case SIMPLE_NAME_REFERENCE:
            case QUALIFIED_NAME_REFERENCE:
                break;
            default:
                this.errorHandler.reportInvalidNode(null, "invalid array length");
                break;
        }
        return lengthExpr;
    }

    private STNode getExpressionAsStatement(STNode expression) {
        switch (expression.kind) {
            case METHOD_CALL:
            case FUNCTION_CALL:
            case CHECK_EXPRESSION:
                return parseCallStatement(expression);
            case REMOTE_METHOD_CALL_ACTION:
            case CHECK_ACTION:
            case BRACED_ACTION:
            case START_ACTION:
            case TRAP_ACTION:
            case FLUSH_ACTION:
            case ASYNC_SEND_ACTION:
            case SYNC_SEND_ACTION:
            case RECEIVE_ACTION:
            case WAIT_ACTION:
            case QUERY_ACTION:
            case COMMIT_ACTION:
                return parseActionStatement(expression);
            default:
                
                
                this.errorHandler.reportInvalidNode(null,
                        "left hand side of an assignment must be a variable reference");

                STNode semicolon = parseSemicolon();
                endContext();
                return STNodeFactory.createExpressionStatementNode(SyntaxKind.INVALID, expression, semicolon);
        }
    }

    /**
     * <p>
     * Parse call statement, given the call expression.
     * <p>
     * <code>
     * call-stmt := call-expr ;
     * <br/>
     * call-expr := function-call-expr | method-call-expr | checking-keyword call-expr
     * </code>
     *
     * @param expression Call expression associated with the call statement
     * @return Call statement node
     */
    private STNode parseCallStatement(STNode expression) {
        validateExprInCallStmt(expression);
        STNode semicolon = parseSemicolon();
        endContext();
        return STNodeFactory.createExpressionStatementNode(SyntaxKind.CALL_STATEMENT, expression, semicolon);
    }

    private void validateExprInCallStmt(STNode expression) {
        switch (expression.kind) {
            case FUNCTION_CALL:
            case METHOD_CALL:
                break;
            case CHECK_EXPRESSION:
                validateExprInCallStmt(((STCheckExpressionNode) expression).expression);
                break;
            case REMOTE_METHOD_CALL_ACTION:
                break;
            case BRACED_EXPRESSION:
                validateExprInCallStmt(((STBracedExpressionNode) expression).expression);
                break;
            default:
                if (isMissingNode(expression)) {
                    break;
                }

                this.errorHandler.reportInvalidNode(null, "expression followed by the checking keyword must be a " +
                        "func-call, a method-call or a check-expr");
                break;
        }
    }

    /**
     * Check whether a node is a missing node.
     *
     * @param node Node to check
     * @return <code>true</code> if the node is a missing node. <code>false</code> otherwise
     */
    private boolean isMissingNode(STNode node) {
        if (node.kind == SyntaxKind.SIMPLE_NAME_REFERENCE) {
            return isMissingNode(((STSimpleNameReferenceNode) node).name);
        }
        return node instanceof STMissingToken;
    }

    private STNode parseActionStatement(STNode action) {
        STNode semicolon = parseSemicolon();
        endContext();
        return STNodeFactory.createExpressionStatementNode(SyntaxKind.ACTION_STATEMENT, action, semicolon);
    }

    /**
     * Parse remote method call action, given the starting expression.
     * <p>
     * <code>
     * remote-method-call-action := expression -> method-name ( arg-list )
     * <br/>
     * async-send-action := expression -> peer-worker ;
     * </code>
     *
     * @param isRhsExpr Is this an RHS action
     * @param expression LHS expression
     * @return
     */
    private STNode parseRemoteMethodCallOrAsyncSendAction(STNode expression, boolean isRhsExpr) {
        STNode rightArrow = parseRightArrow();
        return parseRemoteCallOrAsyncSendActionRhs(peek().kind, expression, isRhsExpr, rightArrow);
    }

    private STNode parseRemoteCallOrAsyncSendActionRhs(SyntaxKind nextTokenKind, STNode expression, boolean isRhsExpr,
                                                       STNode rightArrow) {
        STNode name;
        switch (nextTokenKind) {
            case DEFAULT_KEYWORD:
                name = parseDefaultKeyword();
                return parseAsyncSendAction(expression, rightArrow, name);
            case IDENTIFIER_TOKEN:
                name = STNodeFactory.createSimpleNameReferenceNode(parseFunctionName());
                break;
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.REMOTE_CALL_OR_ASYNC_SEND_RHS, expression,
                        isRhsExpr, rightArrow);

                
                
                
                if (solution.action == Action.REMOVE) {
                    name = solution.recoveredNode;
                    break;
                }

                return parseRemoteCallOrAsyncSendActionRhs(solution.tokenKind, expression, isRhsExpr, rightArrow);
        }

        return parseRemoteCallOrAsyncSendEnd(peek().kind, expression, rightArrow, name);
    }

    private STNode parseRemoteCallOrAsyncSendEnd(SyntaxKind nextTokenKind, STNode expression, STNode rightArrow,
                                                 STNode name) {
        switch (nextTokenKind) {
            case OPEN_PAREN_TOKEN:
                return parseRemoteMethodCallAction(expression, rightArrow, name);
            case SEMICOLON_TOKEN:
                return parseAsyncSendAction(expression, rightArrow, name);
            default:
                STToken token = peek();
                Solution solution =
                        recover(token, ParserRuleContext.REMOTE_CALL_OR_ASYNC_SEND_END, expression, rightArrow, name);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseRemoteCallOrAsyncSendEnd(solution.tokenKind, expression, rightArrow, name);
        }
    }

    /**
     * Parse default keyword.
     *
     * @return default keyword node
     */
    private STNode parseDefaultKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.DEFAULT_KEYWORD) {
            return STNodeFactory.createSimpleNameReferenceNode(consume());
        } else {
            Solution sol = recover(token, ParserRuleContext.DEFAULT_KEYWORD);
            return sol.recoveredNode;
        }
    }

    private STNode parseAsyncSendAction(STNode expression, STNode rightArrow, STNode peerWorker) {
        return STNodeFactory.createAsyncSendActionNode(expression, rightArrow, peerWorker);
    }

    private STNode parseRemoteMethodCallAction(STNode expression, STNode rightArrow, STNode name) {
        STNode openParenToken = parseOpenParenthesis(ParserRuleContext.ARG_LIST_START);
        STNode arguments = parseArgsList();
        STNode closeParenToken = parseCloseParenthesis();
        return STNodeFactory.createRemoteMethodCallActionNode(expression, rightArrow, name, openParenToken, arguments,
                closeParenToken);
    }

    /**
     * Parse right arrow (<code>-></code>) token.
     *
     * @return Parsed node
     */
    private STNode parseRightArrow() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.RIGHT_ARROW_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.RIGHT_ARROW);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse parameterized type descriptor.
     * parameterized-type-descriptor := map type-parameter | future type-parameter | typedesc type-parameter
     *
     * @return Parsed node
     */
    private STNode parseParameterizedTypeDescriptor() {
        STNode parameterizedTypeKeyword = parseParameterizedTypeKeyword();
        STNode ltToken = parseLTToken();
        STNode typeNode = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANGLE_BRACKETS);
        STNode gtToken = parseGTToken();
        return STNodeFactory.createParameterizedTypeDescriptorNode(parameterizedTypeKeyword, ltToken, typeNode,
                gtToken);
    }

    /**
     * Parse <code>map</code> or <code>future</code> or <code>typedesc</code> keyword token.
     *
     * @return Parsed node
     */
    private STNode parseParameterizedTypeKeyword() {
        STToken nextToken = peek();
        switch (nextToken.kind) {
            case MAP_KEYWORD: 
            case FUTURE_KEYWORD: 
            case TYPEDESC_KEYWORD: 
                return consume();
            default:
                Solution sol = recover(nextToken, ParserRuleContext.PARAMETERIZED_TYPE);
                return sol.recoveredNode;
        }
    }

    /**
     * Parse <code> < </code> token.
     *
     * @return Parsed node
     */
    private STNode parseGTToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.GT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.GT);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse <code> > </code> token.
     *
     * @return Parsed node
     */
    private STNode parseLTToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.LT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.LT);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse nil literal. Here nil literal is only referred to ( ).
     *
     * @return Parsed node
     */
    private STNode parseNilLiteral() {
        startContext(ParserRuleContext.NIL_LITERAL);
        STNode openParenthesisToken = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
        STNode closeParenthesisToken = parseCloseParenthesis();
        endContext();
        return STNodeFactory.createNilLiteralNode(openParenthesisToken, closeParenthesisToken);
    }

    /**
     * Parse annotation declaration, given the qualifier.
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the listener declaration
     * @param constKeyword Const keyword
     * @return Parsed node
     */
    private STNode parseAnnotationDeclaration(STNode metadata, STNode qualifier, STNode constKeyword) {
        startContext(ParserRuleContext.ANNOTATION_DECL);
        STNode annotationKeyword = parseAnnotationKeyword();
        STNode annotDecl = parseAnnotationDeclFromType(metadata, qualifier, constKeyword, annotationKeyword);
        endContext();
        return annotDecl;
    }

    /**
     * Parse annotation keyword.
     *
     * @return Parsed node
     */
    private STNode parseAnnotationKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ANNOTATION_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ANNOTATION_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse the components that follows after the annotation keyword of a annotation declaration.
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the constant decl
     * @param constKeyword Const keyword
     * @param annotationKeyword
     * @return Parsed node
     */
    private STNode parseAnnotationDeclFromType(STNode metadata, STNode qualifier, STNode constKeyword,
                                               STNode annotationKeyword) {
        STToken nextToken = peek();
        return parseAnnotationDeclFromType(nextToken.kind, metadata, qualifier, constKeyword, annotationKeyword);
    }

    private STNode parseAnnotationDeclFromType(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
                                               STNode constKeyword, STNode annotationKeyword) {
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN:
                return parseAnnotationDeclWithOptionalType(metadata, qualifier, constKeyword, annotationKeyword);
            default:
                if (isTypeStartingToken(nextTokenKind)) {
                    break;
                }

                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.ANNOT_DECL_OPTIONAL_TYPE, metadata, qualifier,
                        constKeyword, annotationKeyword);
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseAnnotationDeclFromType(solution.tokenKind, metadata, qualifier, constKeyword,
                        annotationKeyword);
        }

        STNode typeDesc = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANNOTATION_DECL);
        STNode annotTag = parseAnnotationTag();
        return parseAnnotationDeclAttachPoints(metadata, qualifier, constKeyword, annotationKeyword, typeDesc,
                annotTag);
    }

    /**
     * Parse annotation tag.
     * <p>
     * <code>annot-tag := identifier</code>
     *
     * @return
     */
    private STNode parseAnnotationTag() {
        STToken token = peek();
        if (token.kind == SyntaxKind.IDENTIFIER_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(peek(), ParserRuleContext.ANNOTATION_TAG);
            return sol.recoveredNode;
        }
    }

    private STNode parseAnnotationDeclWithOptionalType(STNode metadata, STNode qualifier, STNode constKeyword,
                                                       STNode annotationKeyword) {
        
        
        STNode typeDescOrAnnotTag = parseAnnotationTag();
        if (typeDescOrAnnotTag.kind == SyntaxKind.QUALIFIED_NAME_REFERENCE) {
            STNode annotTag = parseAnnotationTag();
            return parseAnnotationDeclAttachPoints(metadata, qualifier, constKeyword, annotationKeyword,
                    typeDescOrAnnotTag, annotTag);
        }

        return parseAnnotationDeclRhs(metadata, qualifier, constKeyword, annotationKeyword, typeDescOrAnnotTag);
    }

    /**
     * Parse the component that follows the first identifier in an annotation decl. The identifier
     * can be either the type-name (a user defined type) or the annot-tag, where the type-name
     * is not present.
     *
     * @param metadata Metadata
     * @param qualifier Qualifier that precedes the annotation decl
     * @param constKeyword Const keyword
     * @param annotationKeyword Annotation keyword
     * @param typeDescOrAnnotTag Identifier that follows the annotation-keyword
     * @return Parsed node
     */
    private STNode parseAnnotationDeclRhs(STNode metadata, STNode qualifier, STNode constKeyword,
                                          STNode annotationKeyword, STNode typeDescOrAnnotTag) {
        STToken token = peek();
        return parseAnnotationDeclRhs(token.kind, metadata, qualifier, constKeyword, annotationKeyword,
                typeDescOrAnnotTag);
    }

    private STNode parseAnnotationDeclRhs(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
                                          STNode constKeyword, STNode annotationKeyword, STNode typeDescOrAnnotTag) {
        STNode typeDesc;
        STNode annotTag;
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN:
                typeDesc = typeDescOrAnnotTag;
                annotTag = parseAnnotationTag();
                break;
            case SEMICOLON_TOKEN:
            case ON_KEYWORD:
                typeDesc = STNodeFactory.createEmptyNode();
                annotTag = typeDescOrAnnotTag;
                break;
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.ANNOT_DECL_RHS, metadata, qualifier, constKeyword,
                        annotationKeyword, typeDescOrAnnotTag);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseAnnotationDeclRhs(solution.tokenKind, metadata, qualifier, constKeyword, annotationKeyword,
                        typeDescOrAnnotTag);
        }

        return parseAnnotationDeclAttachPoints(metadata, qualifier, constKeyword, annotationKeyword, typeDesc,
                annotTag);
    }

    private STNode parseAnnotationDeclAttachPoints(STNode metadata, STNode qualifier, STNode constKeyword,
                                                   STNode annotationKeyword, STNode typeDesc, STNode annotTag) {
        STToken nextToken = peek();
        return parseAnnotationDeclAttachPoints(nextToken.kind, metadata, qualifier, constKeyword, annotationKeyword,
                typeDesc, annotTag);

    }

    private STNode parseAnnotationDeclAttachPoints(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
                                                   STNode constKeyword, STNode annotationKeyword, STNode typeDesc,
                                                   STNode annotTag) {
        STNode onKeyword;
        STNode attachPoints;
        switch (nextTokenKind) {
            case SEMICOLON_TOKEN:
                onKeyword = STNodeFactory.createEmptyNode();
                attachPoints = STNodeFactory.createEmptyNode();
                break;
            case ON_KEYWORD:
                onKeyword = parseOnKeyword();
                attachPoints = parseAnnotationAttachPoints();
                onKeyword = addDiagnosticIfListEmpty(attachPoints, onKeyword,
                        DiagnosticErrorCode.ERROR_MISSING_ANNOTATION_ATTACH_POINT);
                break;
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.ANNOT_OPTIONAL_ATTACH_POINTS, metadata, qualifier,
                        constKeyword, annotationKeyword, typeDesc, annotTag);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseAnnotationDeclAttachPoints(solution.tokenKind, metadata, qualifier, constKeyword,
                        annotationKeyword, typeDesc, annotTag);
        }

        STNode semicolonToken = parseSemicolon();
        return STNodeFactory.createAnnotationDeclarationNode(metadata, qualifier, constKeyword, annotationKeyword,
                typeDesc, annotTag, onKeyword, attachPoints, semicolonToken);
    }

    /**
     * Parse annotation attach points.
     * <p>
     * <code>
     * annot-attach-points := annot-attach-point (, annot-attach-point)*
     * <br/><br/>
     * annot-attach-point := dual-attach-point | source-only-attach-point
     * <br/><br/>
     * dual-attach-point := [source] dual-attach-point-ident
     * <br/><br/>
     * dual-attach-point-ident :=
     *     [object] type
     *     | [object|resource] function
     *     | parameter
     *     | return
     *     | service
     *     | [object|record] field
     * <br/><br/>
     * source-only-attach-point := source source-only-attach-point-ident
     * <br/><br/>
     * source-only-attach-point-ident :=
     *     annotation
     *     | external
     *     | var
     *     | const
     *     | listener
     *     | worker
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseAnnotationAttachPoints() {
        startContext(ParserRuleContext.ANNOT_ATTACH_POINTS_LIST);
        List<STNode> attachPoints = new ArrayList<>();

        STToken nextToken = peek();
        if (isEndAnnotAttachPointList(nextToken.kind)) {
            endContext();
            return STNodeFactory.createEmptyNodeList();
        }

        
        STNode attachPoint = parseAnnotationAttachPoint();
        attachPoints.add(attachPoint);

        
        nextToken = peek();
        STNode leadingComma;
        while (!isEndAnnotAttachPointList(nextToken.kind)) {
            leadingComma = parseAttachPointEnd();
            if (leadingComma == null) {
                break;
            }
            attachPoints.add(leadingComma);

            
            attachPoint = parseAnnotationAttachPoint();
            if (attachPoint == null) {
                attachPoint = errorHandler.createMissingTokenWithDiagnostics(SyntaxKind.IDENTIFIER_TOKEN,
                        DiagnosticErrorCode.ERROR_MISSING_ANNOTATION_ATTACH_POINT);
                attachPoints.add(attachPoint);
                break;
            }

            attachPoints.add(attachPoint);
            nextToken = peek();
        }

        endContext();
        return STNodeFactory.createNodeList(attachPoints);
    }

    /**
     * Parse annotation attach point end.
     *
     * @return Parsed node
     */
    private STNode parseAttachPointEnd() {
        STToken nextToken = peek();
        return parseAttachPointEnd(nextToken.kind);
    }

    private STNode parseAttachPointEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case SEMICOLON_TOKEN:
                
                return null;
            case COMMA_TOKEN:
                return consume();
            default:
                Solution sol = recover(peek(), ParserRuleContext.ATTACH_POINT_END);
                if (sol.action == Action.REMOVE) {
                    return sol.recoveredNode;
                }

                return sol.tokenKind == SyntaxKind.COMMA_TOKEN ? sol.recoveredNode : null;
        }
    }

    private boolean isEndAnnotAttachPointList(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case EOF_TOKEN:
            case SEMICOLON_TOKEN:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse annotation attach point.
     *
     * @return Parsed node
     */
    private STNode parseAnnotationAttachPoint() {
        return parseAnnotationAttachPoint(peek().kind);
    }

    private STNode parseAnnotationAttachPoint(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case EOF_TOKEN:
                return null;

            
            case ANNOTATION_KEYWORD:
            case EXTERNAL_KEYWORD:
            case VAR_KEYWORD:
            case CONST_KEYWORD:
            case LISTENER_KEYWORD:
            case WORKER_KEYWORD:
                

            case SOURCE_KEYWORD:
                STNode sourceKeyword = parseSourceKeyword();
                return parseAttachPointIdent(sourceKeyword);

            
            case OBJECT_KEYWORD:
            case TYPE_KEYWORD:
            case RESOURCE_KEYWORD:
            case FUNCTION_KEYWORD:
            case PARAMETER_KEYWORD:
            case RETURN_KEYWORD:
            case SERVICE_KEYWORD:
            case FIELD_KEYWORD:
            case RECORD_KEYWORD:
                sourceKeyword = STNodeFactory.createEmptyNode();
                STNode firstIdent = consume();
                return parseDualAttachPointIdent(sourceKeyword, firstIdent);
            default:
                Solution solution = recover(peek(), ParserRuleContext.ATTACH_POINT);
                return solution.recoveredNode;
        }
    }

    /**
     * Parse source keyword.
     *
     * @return Parsed node
     */
    private STNode parseSourceKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.SOURCE_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.SOURCE_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse attach point ident gievn.
     * <p>
     * <code>
     * source-only-attach-point-ident := annotation | external | var | const | listener | worker
     * <br/><br/>
     * dual-attach-point-ident := [object] type | [object|resource] function | parameter
     *                            | return | service | [object|record] field
     * </code>
     *
     * @param sourceKeyword Source keyword
     * @return Parsed node
     */
    private STNode parseAttachPointIdent(STNode sourceKeyword) {
        return parseAttachPointIdent(peek().kind, sourceKeyword);
    }

    private STNode parseAttachPointIdent(SyntaxKind nextTokenKind, STNode sourceKeyword) {
        switch (nextTokenKind) {
            case ANNOTATION_KEYWORD:
            case EXTERNAL_KEYWORD:
            case VAR_KEYWORD:
            case CONST_KEYWORD:
            case LISTENER_KEYWORD:
            case WORKER_KEYWORD:
                STNode firstIdent = consume();
                STNode secondIdent = STNodeFactory.createEmptyNode();
                return STNodeFactory.createAnnotationAttachPointNode(sourceKeyword, firstIdent, secondIdent);
            case OBJECT_KEYWORD:
            case RESOURCE_KEYWORD:
            case RECORD_KEYWORD:
            case TYPE_KEYWORD:
            case FUNCTION_KEYWORD:
            case PARAMETER_KEYWORD:
            case RETURN_KEYWORD:
            case SERVICE_KEYWORD:
            case FIELD_KEYWORD:
                firstIdent = consume();
                return parseDualAttachPointIdent(sourceKeyword, firstIdent);
            default:
                Solution solution = recover(peek(), ParserRuleContext.ATTACH_POINT_IDENT, sourceKeyword);
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                firstIdent = solution.recoveredNode;
                return parseDualAttachPointIdent(sourceKeyword, firstIdent);
        }
    }

    /**
     * Parse dual-attach-point ident.
     *
     * @param sourceKeyword Source keyword
     * @param firstIdent first part of the dual attach-point
     * @return Parsed node
     */
    private STNode parseDualAttachPointIdent(STNode sourceKeyword, STNode firstIdent) {
        STNode secondIdent;
        switch (firstIdent.kind) {
            case OBJECT_KEYWORD:
                secondIdent = parseIdentAfterObjectIdent();
                break;
            case RESOURCE_KEYWORD:
                secondIdent = parseFunctionIdent();
                break;
            case RECORD_KEYWORD:
                secondIdent = parseFieldIdent();
                break;
            case TYPE_KEYWORD:
            case FUNCTION_KEYWORD:
            case PARAMETER_KEYWORD:
            case RETURN_KEYWORD:
            case SERVICE_KEYWORD:
            case FIELD_KEYWORD:
            default: 
                secondIdent = STNodeFactory.createEmptyNode();
                break;
        }

        return STNodeFactory.createAnnotationAttachPointNode(sourceKeyword, firstIdent, secondIdent);
    }

    /**
     * Parse the idents that are supported after object-ident.
     *
     * @return Parsed node
     */
    private STNode parseIdentAfterObjectIdent() {
        STToken token = peek();
        switch (token.kind) {
            case TYPE_KEYWORD:
            case FUNCTION_KEYWORD:
            case FIELD_KEYWORD:
                return consume();
            default:
                Solution sol = recover(token, ParserRuleContext.IDENT_AFTER_OBJECT_IDENT);
                return sol.recoveredNode;
        }
    }

    /**
     * Parse function ident.
     *
     * @return Parsed node
     */
    private STNode parseFunctionIdent() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FUNCTION_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FUNCTION_IDENT);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse field ident.
     *
     * @return Parsed node
     */
    private STNode parseFieldIdent() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FIELD_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FIELD_IDENT);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse XML namespace declaration.
     * <p>
     * <code>xmlns-decl := xmlns xml-namespace-uri [ as xml-namespace-prefix ] ;
     * <br/>
     * xml-namespace-uri := simple-const-expr
     * <br/>
     * xml-namespace-prefix := identifier
     * </code>
     *
     * @return
     */
    private STNode parseXMLNamepsaceDeclaration() {
        startContext(ParserRuleContext.XML_NAMESPACE_DECLARATION);
        STNode xmlnsKeyword = parseXMLNSKeyword();
        STNode namespaceUri = parseXMLNamespaceUri();
        STNode xmlnsDecl = parseXMLDeclRhs(xmlnsKeyword, namespaceUri);
        endContext();
        return xmlnsDecl;
    }

    /**
     * Parse xmlns keyword.
     *
     * @return Parsed node
     */
    private STNode parseXMLNSKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.XMLNS_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.XMLNS_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse namespace uri.
     *
     * @return Parsed node
     */
    private STNode parseXMLNamespaceUri() {
        STNode expr = parseConstExpr();
        switch (expr.kind) {
            case STRING_LITERAL:
            case IDENTIFIER_TOKEN:
            case QUALIFIED_NAME_REFERENCE:
                break;
            default:
                this.errorHandler.reportInvalidNode(null, "namespace uri must be a subtype of string");
        }

        return expr;
    }

    private STNode parseConstExpr() {
        startContext(ParserRuleContext.CONSTANT_EXPRESSION);
        STNode expr = parseConstExprInternal();
        endContext();
        return expr;
    }

    private STNode parseConstExprInternal() {
        STToken nextToken = peek();
        return parseConstExprInternal(nextToken.kind);
    }

    /**
     * Parse constants expr.
     *
     * @return Parsed node
     */
    private STNode parseConstExprInternal(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case STRING_LITERAL:
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case DECIMAL_FLOATING_POINT_LITERAL:
            case HEX_FLOATING_POINT_LITERAL:
            case TRUE_KEYWORD:
            case FALSE_KEYWORD:
            case NULL_KEYWORD:
                return parseBasicLiteral();
            case IDENTIFIER_TOKEN:
                return parseQualifiedIdentifier(ParserRuleContext.VARIABLE_REF);
            case PLUS_TOKEN:
            case MINUS_TOKEN:
                return parseSignedIntOrFloat();
            case OPEN_BRACE_TOKEN:
                return parseNilLiteral();
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.CONSTANT_EXPRESSION_START);
                return solution.recoveredNode;
        }
    }

    /**
     * Parse the portion after the namsepsace-uri of an XML declaration.
     *
     * @param xmlnsKeyword XMLNS keyword
     * @param namespaceUri Namespace URI
     * @return Parsed node
     */
    private STNode parseXMLDeclRhs(STNode xmlnsKeyword, STNode namespaceUri) {
        return parseXMLDeclRhs(peek().kind, xmlnsKeyword, namespaceUri);
    }

    private STNode parseXMLDeclRhs(SyntaxKind nextTokenKind, STNode xmlnsKeyword, STNode namespaceUri) {
        STNode asKeyword = STNodeFactory.createEmptyNode();
        STNode namespacePrefix = STNodeFactory.createEmptyNode();

        switch (nextTokenKind) {
            case AS_KEYWORD:
                asKeyword = parseAsKeyword();
                namespacePrefix = parseNamespacePrefix();
                break;
            case SEMICOLON_TOKEN:
                break;
            default:
                STToken token = peek();
                Solution solution =
                        recover(token, ParserRuleContext.XML_NAMESPACE_PREFIX_DECL, xmlnsKeyword, namespaceUri);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseXMLDeclRhs(solution.tokenKind, xmlnsKeyword, namespaceUri);
        }
        STNode semicolon = parseSemicolon();
        return STNodeFactory.createXMLNamespaceDeclarationNode(xmlnsKeyword, namespaceUri, asKeyword, namespacePrefix,
                semicolon);
    }

    /**
     * Parse import prefix.
     *
     * @return Parsed node
     */
    private STNode parseNamespacePrefix() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.IDENTIFIER_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(peek(), ParserRuleContext.NAMESPACE_PREFIX);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse named worker declaration.
     * <p>
     * <code>named-worker-decl := [annots] worker worker-name return-type-descriptor { sequence-stmt }</code>
     *
     * @param annots Annotations attached to the worker decl
     * @return Parsed node
     */
    private STNode parseNamedWorkerDeclaration(STNode annots) {
        startContext(ParserRuleContext.NAMED_WORKER_DECL);
        STNode workerKeyword = parseWorkerKeyword();
        STNode workerName = parseWorkerName();
        STNode returnTypeDesc = parseReturnTypeDescriptor();
        STNode workerBody = parseBlockNode();
        endContext();
        return STNodeFactory.createNamedWorkerDeclarationNode(annots, workerKeyword, workerName, returnTypeDesc,
                workerBody);
    }

    private STNode parseReturnTypeDescriptor() {
        
        STToken token = peek();
        if (token.kind != SyntaxKind.RETURNS_KEYWORD) {
            return STNodeFactory.createEmptyNode();
        }

        STNode returnsKeyword = consume();
        STNode annot = parseAnnotations();
        STNode type = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_RETURN_TYPE_DESC);
        return STNodeFactory.createReturnTypeDescriptorNode(returnsKeyword, annot, type);
    }

    /**
     * Parse worker keyword.
     *
     * @return Parsed node
     */
    private STNode parseWorkerKeyword() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.WORKER_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(peek(), ParserRuleContext.WORKER_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse worker name.
     * <p>
     * <code>worker-name := identifier</code>
     *
     * @return Parsed node
     */
    private STNode parseWorkerName() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.IDENTIFIER_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(peek(), ParserRuleContext.WORKER_NAME);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse documentation string.
     * <p>
     * <code>DocumentationString := DocumentationLine +</code>
     * <p>
     * Refer {@link BallerinaLexer
     *
     * @return Parsed node
     */
    private STNode parseDocumentationString() {
        List<STNode> docLines = new ArrayList<>();
        STToken nextToken = peek();
        while (nextToken.kind == SyntaxKind.DOCUMENTATION_LINE) {
            docLines.add(consume());
            nextToken = peek();
        }

        STNode documentationLines = STNodeFactory.createNodeList(docLines);
        return STNodeFactory.createDocumentationStringNode(documentationLines);
    }

    private STNode createNilLiteral(STNode openParenthesisToken, STNode closeParenthesisToken) {
        return STNodeFactory.createNilLiteralNode(openParenthesisToken, closeParenthesisToken);
    }

    /**
     * Parse lock statement.
     * <code>lock-stmt := lock block-stmt ;</code>
     *
     * @return Lock statement
     */
    private STNode parseLockStatement() {
        startContext(ParserRuleContext.LOCK_STMT);
        STNode lockKeyword = parseLockKeyword();
        STNode blockStatement = parseBlockNode();
        endContext();
        return STNodeFactory.createLockStatementNode(lockKeyword, blockStatement);
    }

    /**
     * Parse lock-keyword.
     *
     * @return lock-keyword node
     */
    private STNode parseLockKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.LOCK_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.LOCK_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse union type descriptor.
     * union-type-descriptor := type-descriptor | type-descriptor
     *
     * @param leftTypeDesc Type desc in the LHS os the union type desc.
     * @param context Current context.
     * @return parsed union type desc node
     */
    private STNode parseUnionTypeDescriptor(STNode leftTypeDesc, ParserRuleContext context) {
        STNode pipeToken = parsePipeToken();
        STNode rightTypeDesc = parseTypeDescriptor(context);

        return STNodeFactory.createUnionTypeDescriptorNode(leftTypeDesc, pipeToken, rightTypeDesc);
    }

    /**
     * Parse pipe token.
     *
     * @return parsed pipe token node
     */
    private STNode parsePipeToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.PIPE_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.PIPE);
            return sol.recoveredNode;
        }
    }

    private boolean isTypeStartingToken(SyntaxKind nodeKind) {
        switch (nodeKind) {
            case IDENTIFIER_TOKEN:
            case SERVICE_KEYWORD:
            case RECORD_KEYWORD:
            case OBJECT_KEYWORD:
            case ABSTRACT_KEYWORD:
            case CLIENT_KEYWORD:
            case OPEN_PAREN_TOKEN: 
            case MAP_KEYWORD: 
            case FUTURE_KEYWORD: 
            case TYPEDESC_KEYWORD: 
            case ERROR_KEYWORD: 
            case STREAM_KEYWORD: 
            case TABLE_KEYWORD: 
            case FUNCTION_KEYWORD:
            case OPEN_BRACKET_TOKEN:
                return true;
            default:
                if (isSingletonTypeDescStart(nodeKind, true)) {
                    return true;
                }
                return isSimpleType(nodeKind);
        }
    }

    static boolean isSimpleType(SyntaxKind nodeKind) {
        switch (nodeKind) {
            case INT_KEYWORD:
            case FLOAT_KEYWORD:
            case DECIMAL_KEYWORD:
            case BOOLEAN_KEYWORD:
            case STRING_KEYWORD:
            case BYTE_KEYWORD:
            case XML_KEYWORD:
            case JSON_KEYWORD:
            case HANDLE_KEYWORD:
            case ANY_KEYWORD:
            case ANYDATA_KEYWORD:
            case NEVER_KEYWORD:
            case SERVICE_KEYWORD:
            case VAR_KEYWORD:
            case ERROR_KEYWORD: 
            case STREAM_KEYWORD: 
            case READONLY_KEYWORD:
            case DISTINCT_KEYWORD:
                return true;
            case TYPE_DESC:
                
                
                
                return true;
            default:
                return false;
        }
    }

    private SyntaxKind getTypeSyntaxKind(SyntaxKind typeKeyword) {
        switch (typeKeyword) {
            case INT_KEYWORD:
                return SyntaxKind.INT_TYPE_DESC;
            case FLOAT_KEYWORD:
                return SyntaxKind.FLOAT_TYPE_DESC;
            case DECIMAL_KEYWORD:
                return SyntaxKind.DECIMAL_TYPE_DESC;
            case BOOLEAN_KEYWORD:
                return SyntaxKind.BOOLEAN_TYPE_DESC;
            case STRING_KEYWORD:
                return SyntaxKind.STRING_TYPE_DESC;
            case BYTE_KEYWORD:
                return SyntaxKind.BYTE_TYPE_DESC;
            case XML_KEYWORD:
                return SyntaxKind.XML_TYPE_DESC;
            case JSON_KEYWORD:
                return SyntaxKind.JSON_TYPE_DESC;
            case HANDLE_KEYWORD:
                return SyntaxKind.HANDLE_TYPE_DESC;
            case ANY_KEYWORD:
                return SyntaxKind.ANY_TYPE_DESC;
            case ANYDATA_KEYWORD:
                return SyntaxKind.ANYDATA_TYPE_DESC;
            case NEVER_KEYWORD:
                return SyntaxKind.NEVER_TYPE_DESC;
            case SERVICE_KEYWORD:
                return SyntaxKind.SERVICE_TYPE_DESC;
            case VAR_KEYWORD:
                return SyntaxKind.VAR_TYPE_DESC;
            default:
                return SyntaxKind.TYPE_DESC;
        }
    }

    /**
     * Parse fork-keyword.
     *
     * @return Fork-keyword node
     */
    private STNode parseForkKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FORK_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FORK_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse multiple named worker declarations.
     *
     * @return named-worker-declarations node array
     */
    private STNode parseMultipleNamedWorkerDeclarations() {
        ArrayList<STNode> workers = new ArrayList<>();
        while (!isEndOfStatements()) {
            STNode stmt = parseStatement();
            if (stmt == null) {
                break;
            }

            switch (stmt.kind) {
                case NAMED_WORKER_DECLARATION:
                    workers.add(stmt);
                    break;
                default:
                    this.errorHandler.reportInvalidNode(null, "Only named-workers are allowed here");
                    break;
            }
        }

        if (workers.isEmpty()) {
            this.errorHandler.reportInvalidNode(null, "Fork Statement must contain atleast one named-worker");
        }
        STNode namedWorkers = STNodeFactory.createNodeList(workers);
        return namedWorkers;
    }

    /**
     * Parse fork statement.
     * <code>fork-stmt := fork { named-worker-decl+ }</code>
     *
     * @return Fork statement
     */
    private STNode parseForkStatement() {
        startContext(ParserRuleContext.FORK_STMT);
        STNode forkKeyword = parseForkKeyword();
        STNode openBrace = parseOpenBrace();
        STNode namedWorkerDeclarations = parseMultipleNamedWorkerDeclarations();
        STNode closeBrace = parseCloseBrace();
        endContext();
        return STNodeFactory.createForkStatementNode(forkKeyword, openBrace, namedWorkerDeclarations, closeBrace);
    }

    /**
     * Parse decimal floating point literal.
     *
     * @return Parsed node
     */
    private STNode parseDecimalFloatingPointLiteral() {
        STToken token = peek();
        if (token.kind == SyntaxKind.DECIMAL_FLOATING_POINT_LITERAL) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.DECIMAL_FLOATING_POINT_LITERAL);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse hex floating point literal.
     *
     * @return Parsed node
     */
    private STNode parseHexFloatingPointLiteral() {
        STToken token = peek();
        if (token.kind == SyntaxKind.HEX_FLOATING_POINT_LITERAL) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.HEX_FLOATING_POINT_LITERAL);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse trap expression.
     * <p>
     * <code>
     * trap-expr := trap expression
     * </code>
     *
     * @param allowActions Allow actions
     * @param isRhsExpr Whether this is a RHS expression or not
     * @return Trap expression node
     */
    private STNode parseTrapExpression(boolean isRhsExpr, boolean allowActions) {
        STNode trapKeyword = parseTrapKeyword();
        STNode expr = parseExpression(OperatorPrecedence.EXPRESSION_ACTION, isRhsExpr, allowActions);
        if (isAction(expr)) {
            return STNodeFactory.createTrapExpressionNode(SyntaxKind.TRAP_ACTION, trapKeyword, expr);
        }

        return STNodeFactory.createTrapExpressionNode(SyntaxKind.TRAP_EXPRESSION, trapKeyword, expr);
    }

    /**
     * Parse trap-keyword.
     *
     * @return Trap-keyword node
     */
    private STNode parseTrapKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.TRAP_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.TRAP_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse list constructor expression.
     * <p>
     * <code>
     * list-constructor-expr := [ [ expr-list ] ]
     * <br/>
     * expr-list := expression (, expression)*
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseListConstructorExpr() {
        startContext(ParserRuleContext.LIST_CONSTRUCTOR);
        STNode openBracket = parseOpenBracket();
        STNode expressions = parseOptionalExpressionsList();
        STNode closeBracket = parseCloseBracket();
        endContext();
        return STNodeFactory.createListConstructorExpressionNode(openBracket, expressions, closeBracket);
    }

    /**
     * Parse optional expression list.
     *
     * @return Parsed node
     */
    private STNode parseOptionalExpressionsList() {
        List<STNode> expressions = new ArrayList<>();
        if (isEndOfListConstructor(peek().kind)) {
            return STNodeFactory.createNodeList(expressions);
        }

        STNode expr = parseExpression();
        expressions.add(expr);
        return parseOptionalExpressionsList(expressions);
    }

    private STNode parseOptionalExpressionsList(List<STNode> expressions) {
        
        STNode listConstructorMemberEnd;
        while (!isEndOfListConstructor(peek().kind)) {

            listConstructorMemberEnd = parseListConstructorMemberEnd();
            if (listConstructorMemberEnd == null) {
                break;
            }
            expressions.add(listConstructorMemberEnd);

            STNode expr = parseExpression();
            expressions.add(expr);
        }

        return STNodeFactory.createNodeList(expressions);
    }

    private boolean isEndOfListConstructor(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACKET_TOKEN:
                return true;
            default:
                return false;
        }
    }

    private STNode parseListConstructorMemberEnd() {
        return parseListConstructorMemberEnd(peek().kind);
    }

    private STNode parseListConstructorMemberEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACKET_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.LIST_CONSTRUCTOR_MEMBER_END);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }
                return parseListConstructorMemberEnd(solution.tokenKind);
        }
    }

    /**
     * Parse foreach statement.
     * <code>foreach-stmt := foreach typed-binding-pattern in action-or-expr block-stmt</code>
     *
     * @return foreach statement
     */
    private STNode parseForEachStatement() {
        startContext(ParserRuleContext.FOREACH_STMT);
        STNode forEachKeyword = parseForEachKeyword();
        STNode typedBindingPattern = parseTypedBindingPattern(ParserRuleContext.FOREACH_STMT);
        STNode inKeyword = parseInKeyword();
        STNode actionOrExpr = parseActionOrExpression();
        STNode blockStatement = parseBlockNode();
        endContext();
        return STNodeFactory.createForEachStatementNode(forEachKeyword, typedBindingPattern, inKeyword, actionOrExpr,
                blockStatement);
    }

    /**
     * Parse foreach-keyword.
     *
     * @return ForEach-keyword node
     */
    private STNode parseForEachKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FOREACH_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FOREACH_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse in-keyword.
     *
     * @return In-keyword node
     */
    private STNode parseInKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.IN_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.IN_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse type cast expression.
     * <p>
     * <code>
     * type-cast-expr := < type-cast-param > expression
     * <br/>
     * type-cast-param := [annots] type-descriptor | annots
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseTypeCastExpr(boolean isRhsExpr) {
        startContext(ParserRuleContext.TYPE_CAST);
        STNode ltToken = parseLTToken();
        STNode typeCastParam = parseTypeCastParam();
        STNode gtToken = parseGTToken();
        endContext();

        
        
        STNode expression = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false);
        return STNodeFactory.createTypeCastExpressionNode(ltToken, typeCastParam, gtToken, expression);
    }

    private STNode parseTypeCastParam() {
        STNode annot;
        STNode type;
        STToken token = peek();

        switch (token.kind) {
            case AT_TOKEN:
                annot = parseAnnotations();
                token = peek();
                if (isTypeStartingToken(token.kind)) {
                    type = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANGLE_BRACKETS);
                } else {
                    type = STNodeFactory.createEmptyNode();
                }
                break;
            default:
                annot = STNodeFactory.createEmptyNode();
                type = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANGLE_BRACKETS);
                break;
        }

        return STNodeFactory.createTypeCastParamNode(getAnnotations(annot), type);
    }

    /**
     * Parse table constructor expression.
     * <p>
     * <code>
     * table-constructor-expr-rhs := [ [row-list] ]
     * </code>
     *
     * @param tableKeyword tableKeyword that precedes this rhs
     * @param keySpecifier keySpecifier that precedes this rhs
     * @return Parsed node
     */
    private STNode parseTableConstructorExprRhs(STNode tableKeyword, STNode keySpecifier) {
        switchContext(ParserRuleContext.TABLE_CONSTRUCTOR);
        STNode openBracket = parseOpenBracket();
        STNode rowList = parseRowList();
        STNode closeBracket = parseCloseBracket();
        return STNodeFactory.createTableConstructorExpressionNode(tableKeyword, keySpecifier, openBracket, rowList,
                closeBracket);
    }

    /**
     * Parse table-keyword.
     *
     * @return Table-keyword node
     */
    private STNode parseTableKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.TABLE_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.TABLE_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse table rows.
     * <p>
     * <code>row-list := [ mapping-constructor-expr (, mapping-constructor-expr)* ]</code>
     *
     * @return Parsed node
     */
    private STNode parseRowList() {
        List<STNode> mappings = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfTableRowList(nextToken.kind)) {
            return STNodeFactory.createNodeList(new ArrayList<>());
        }

        
        STNode mapExpr = parseMappingConstructorExpr();
        mappings.add(mapExpr);

        
        nextToken = peek();
        STNode leadingComma;
        while (!isEndOfTableRowList(nextToken.kind)) {
            leadingComma = parseComma();
            mappings.add(leadingComma);
            mapExpr = parseMappingConstructorExpr();
            mappings.add(mapExpr);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(mappings);
    }

    private boolean isEndOfTableRowList(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACKET_TOKEN:
                return true;
            case COMMA_TOKEN:
            case OPEN_BRACE_TOKEN:
                return false;
            default:
                return isEndOfMappingConstructor(tokenKind);
        }
    }

    /**
     * Parse key specifier.
     * <p>
     * <code>key-specifier := key ( [ field-name (, field-name)* ] )</code>
     *
     * @return Parsed node
     */
    private STNode parseKeySpecifier() {
        startContext(ParserRuleContext.KEY_SPECIFIER);
        STNode keyKeyword = parseKeyKeyword();
        STNode openParen = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
        STNode fieldNames = parseFieldNames();
        STNode closeParen = parseCloseParenthesis();
        endContext();
        return STNodeFactory.createKeySpecifierNode(keyKeyword, openParen, fieldNames, closeParen);
    }

    /**
     * Parse key-keyword.
     *
     * @return Key-keyword node
     */
    private STNode parseKeyKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.KEY_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.KEY_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse field names.
     * <p>
     * <code>field-name-list := [ field-name (, field-name)* ]</code>
     *
     * @return Parsed node
     */
    private STNode parseFieldNames() {
        List<STNode> fieldNames = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfFieldNamesList(nextToken.kind)) {
            return STNodeFactory.createNodeList(new ArrayList<>());
        }

        
        STNode fieldName = parseVariableName();
        fieldNames.add(fieldName);

        
        nextToken = peek();
        STNode leadingComma;
        while (!isEndOfFieldNamesList(nextToken.kind)) {
            leadingComma = parseComma();
            fieldNames.add(leadingComma);
            fieldName = parseVariableName();
            fieldNames.add(fieldName);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(fieldNames);
    }

    private boolean isEndOfFieldNamesList(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case COMMA_TOKEN:
            case IDENTIFIER_TOKEN:
                return false;
            default:
                return true;
        }
    }

    /**
     * Parse error type descriptor.
     * <p>
     * error-type-descriptor := error [error-type-param]
     * error-type-param := < (detail-type-descriptor | inferred-type-descriptor) >
     * detail-type-descriptor := type-descriptor
     * inferred-type-descriptor := *
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseErrorTypeDescriptor() {
        STNode errorKeywordToken = parseErrorKeyWord();
        STNode errorTypeParamsNode;
        STToken nextToken = peek();
        STToken nextNextToken = peek(2);
        if (nextToken.kind == SyntaxKind.LT_TOKEN || nextNextToken.kind == SyntaxKind.GT_TOKEN) {
            errorTypeParamsNode = parseErrorTypeParamsNode();
        } else {
            errorTypeParamsNode = STNodeFactory.createEmptyNode();
        }
        return STNodeFactory.createErrorTypeDescriptorNode(errorKeywordToken, errorTypeParamsNode);
    }

    /**
     * Parse error type param node.
     * <p>
     * error-type-param := < (detail-type-descriptor | inferred-type-descriptor) >
     * detail-type-descriptor := type-descriptor
     * inferred-type-descriptor := *
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseErrorTypeParamsNode() {
        STNode ltToken = parseLTToken();
        STNode parameter;
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.ASTERISK_TOKEN) {
            parameter = consume();
        } else {
            parameter = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANGLE_BRACKETS);
        }
        STNode gtToken = parseGTToken();
        return STNodeFactory.createErrorTypeParamsNode(ltToken, parameter, gtToken);
    }

    /**
     * Parse error-keyword.
     *
     * @return Parsed error-keyword node
     */
    private STNode parseErrorKeyWord() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ERROR_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ERROR_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse stream type descriptor.
     * <p>
     * stream-type-descriptor := stream [stream-type-parameters]
     * stream-type-parameters := < type-descriptor [, type-descriptor]>
     * </p>
     *
     * @return Parsed stream type descriptor node
     */
    private STNode parseStreamTypeDescriptor() {
        STNode streamKeywordToken = parseStreamKeyword();
        STNode streamTypeParamsNode;
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.LT_TOKEN) {
            streamTypeParamsNode = parseStreamTypeParamsNode();
        } else {
            streamTypeParamsNode = STNodeFactory.createEmptyNode();
        }
        return STNodeFactory.createStreamTypeDescriptorNode(streamKeywordToken, streamTypeParamsNode);
    }

    /**
     * Parse stream type params node.
     * <p>
     * stream-type-parameters := < type-descriptor [, type-descriptor]>
     * </p>
     *
     * @return Parsed stream type params node
     */
    private STNode parseStreamTypeParamsNode() {
        STNode ltToken = parseLTToken();
        startContext(ParserRuleContext.TYPE_DESC_IN_STREAM_TYPE_DESC);
        STNode leftTypeDescNode = parseTypeDescriptorInternal(ParserRuleContext.TYPE_DESC_IN_STREAM_TYPE_DESC);
        STNode streamTypedesc = parseStreamTypeParamsNode(ltToken, leftTypeDescNode);
        endContext();
        return streamTypedesc;
    }

    private STNode parseStreamTypeParamsNode(STNode ltToken, STNode leftTypeDescNode) {
        return parseStreamTypeParamsNode(peek().kind, ltToken, leftTypeDescNode);
    }

    private STNode parseStreamTypeParamsNode(SyntaxKind nextTokenKind, STNode ltToken, STNode leftTypeDescNode) {
        STNode commaToken, rightTypeDescNode, gtToken;

        switch (nextTokenKind) {
            case COMMA_TOKEN:
                commaToken = parseComma();
                rightTypeDescNode = parseTypeDescriptorInternal(ParserRuleContext.TYPE_DESC_IN_STREAM_TYPE_DESC);
                break;
            case GT_TOKEN:
                commaToken = STNodeFactory.createEmptyNode();
                rightTypeDescNode = STNodeFactory.createEmptyNode();
                break;
            default:
                Solution solution =
                        recover(peek(), ParserRuleContext.STREAM_TYPE_FIRST_PARAM_RHS, ltToken, leftTypeDescNode);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }
                return parseStreamTypeParamsNode(solution.tokenKind, ltToken, leftTypeDescNode);
        }

        gtToken = parseGTToken();
        return STNodeFactory.createStreamTypeParamsNode(ltToken, leftTypeDescNode, commaToken, rightTypeDescNode,
                gtToken);
    }

    /**
     * Parse stream-keyword.
     *
     * @return Parsed stream-keyword node
     */
    private STNode parseStreamKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.STREAM_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.STREAM_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse let expression.
     * <p>
     * <code>
     * let-expr := let let-var-decl [, let-var-decl]* in expression
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseLetExpression(boolean isRhsExpr) {
        STNode letKeyword = parseLetKeyword();
        STNode letVarDeclarations = parseLetVarDeclarations(ParserRuleContext.LET_EXPR_LET_VAR_DECL, isRhsExpr);
        STNode inKeyword = parseInKeyword();

        
        letKeyword = addDiagnosticIfListEmpty(letVarDeclarations, letKeyword,
                DiagnosticErrorCode.ERROR_MISSING_LET_VARIABLE_DECLARATION);

        
        
        STNode expression = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false);
        return STNodeFactory.createLetExpressionNode(letKeyword, letVarDeclarations, inKeyword, expression);
    }

    /**
     * Parse let-keyword.
     *
     * @return Let-keyword node
     */
    private STNode parseLetKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.LET_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.LET_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse let variable declarations.
     * <p>
     * <code>let-var-decl-list := let-var-decl [, let-var-decl]*</code>
     *
     * @return Parsed node
     */
    private STNode parseLetVarDeclarations(ParserRuleContext context, boolean isRhsExpr) {
        startContext(context);
        List<STNode> varDecls = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfLetVarDeclarations(nextToken.kind)) {
            endContext();
            return STNodeFactory.createEmptyNodeList();
        }

        
        STNode varDec = parseLetVarDecl(isRhsExpr);
        varDecls.add(varDec);

        
        nextToken = peek();
        STNode leadingComma;
        while (!isEndOfLetVarDeclarations(nextToken.kind)) {
            leadingComma = parseComma();
            varDecls.add(leadingComma);
            varDec = parseLetVarDecl(isRhsExpr);
            varDecls.add(varDec);
            nextToken = peek();
        }

        endContext();
        return STNodeFactory.createNodeList(varDecls);
    }

    private boolean isEndOfLetVarDeclarations(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case COMMA_TOKEN:
            case AT_TOKEN:
                return false;
            case IN_KEYWORD:
                return true;
            default:
                return !isTypeStartingToken(tokenKind);
        }
    }

    /**
     * Parse let variable declaration.
     * <p>
     * <code>let-var-decl := [annots] typed-binding-pattern = expression</code>
     *
     * @return Parsed node
     */
    private STNode parseLetVarDecl(boolean isRhsExpr) {
        STNode annot = parseAnnotations();
        STNode typedBindingPattern = parseTypedBindingPattern(ParserRuleContext.LET_EXPR_LET_VAR_DECL);
        STNode assign = parseAssignOp();

        
        
        STNode expression = parseExpression(OperatorPrecedence.ANON_FUNC_OR_LET, isRhsExpr, false);
        return STNodeFactory.createLetVariableDeclarationNode(annot, typedBindingPattern, assign, expression);
    }

    /**
     * Parse raw backtick string template expression.
     * <p>
     * <code>BacktickString := `expression`</code>
     *
     * @return Template expression node
     */
    private STNode parseTemplateExpression() {
        STNode type = STNodeFactory.createEmptyNode();
        STNode startingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_START);
        STNode content = parseTemplateContent();
        STNode endingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_START);
        return STNodeFactory.createTemplateExpressionNode(SyntaxKind.RAW_TEMPLATE_EXPRESSION, type, startingBackTick,
                content, endingBackTick);
    }

    private STNode parseTemplateContent() {
        List<STNode> items = new ArrayList<>();
        STToken nextToken = peek();
        while (!isEndOfBacktickContent(nextToken.kind)) {
            STNode contentItem = parseTemplateItem();
            items.add(contentItem);
            nextToken = peek();
        }
        return STNodeFactory.createNodeList(items);
    }

    private boolean isEndOfBacktickContent(SyntaxKind kind) {
        switch (kind) {
            case EOF_TOKEN:
            case BACKTICK_TOKEN:
                return true;
            default:
                return false;
        }
    }

    private STNode parseTemplateItem() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.INTERPOLATION_START_TOKEN) {
            return parseInterpolation();
        }

        
        return consume();
    }

    /**
     * Parse string template expression.
     * <p>
     * <code>string-template-expr := string ` expression `</code>
     *
     * @return String template expression node
     */
    private STNode parseStringTemplateExpression() {
        STNode type = parseStringKeyword();
        STNode startingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_START);
        STNode content = parseTemplateContent();
        STNode endingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_END);
        return STNodeFactory.createTemplateExpressionNode(SyntaxKind.STRING_TEMPLATE_EXPRESSION, type, startingBackTick,
                content, endingBackTick);
    }

    /**
     * Parse <code>string</code> keyword.
     *
     * @return string keyword node
     */
    private STNode parseStringKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.STRING_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.STRING_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse XML template expression.
     * <p>
     * <code>xml-template-expr := xml BacktickString</code>
     *
     * @return XML template expression
     */
    private STNode parseXMLTemplateExpression() {
        STNode xmlKeyword = parseXMLKeyword();
        STNode startingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_START);
        STNode content = parseTemplateContentAsXML();
        STNode endingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_END);
        return STNodeFactory.createTemplateExpressionNode(SyntaxKind.XML_TEMPLATE_EXPRESSION, xmlKeyword,
                startingBackTick, content, endingBackTick);
    }

    /**
     * Parse <code>xml</code> keyword.
     *
     * @return xml keyword node
     */
    private STNode parseXMLKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.XML_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.XML_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse the content of the template string as XML. This method first read the
     * input in the same way as the raw-backtick-template (BacktickString). Then
     * it parses the content as XML.
     *
     * @return XML node
     */
    private STNode parseTemplateContentAsXML() {
        
        
        
        ArrayDeque<STNode> expressions = new ArrayDeque<>();
        StringBuilder xmlStringBuilder = new StringBuilder();
        STToken nextToken = peek();
        while (!isEndOfBacktickContent(nextToken.kind)) {
            STNode contentItem = parseTemplateItem();
            if (contentItem.kind == SyntaxKind.TEMPLATE_STRING) {
                xmlStringBuilder.append(((STToken) contentItem).text());
            } else {
                xmlStringBuilder.append("${}");
                expressions.add(contentItem);
            }
            nextToken = peek();
        }

        TextDocument textDocument = TextDocuments.from(xmlStringBuilder.toString());
        AbstractTokenReader tokenReader = new TokenReader(new XMLLexer(textDocument.getCharacterReader()));
        XMLParser xmlParser = new XMLParser(tokenReader, expressions);
        return xmlParser.parse();
    }

    /**
     * Parse interpolation of a back-tick string.
     * <p>
     * <code>
     * interpolation := ${ expression }
     * </code>
     *
     * @return Interpolation node
     */
    private STNode parseInterpolation() {
        startContext(ParserRuleContext.INTERPOLATION);
        STNode interpolStart = parseInterpolationStart();
        STNode expr = parseExpression();
        removeAdditionalTokensInInterpolation();
        STNode closeBrace = parseCloseBrace();
        endContext();
        return STNodeFactory.createInterpolationNode(interpolStart, expr, closeBrace);
    }

    /**
     * Parse interpolation start token.
     * <p>
     * <code>interpolation-start := ${</code>
     *
     * @return Interpolation start token
     */
    private STNode parseInterpolationStart() {
        STToken token = peek();
        if (token.kind == SyntaxKind.INTERPOLATION_START_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.INTERPOLATION_START_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Remove if there any tokens left after the expression inside the interpolation.
     */
    private void removeAdditionalTokensInInterpolation() {
        while (true) {
            STToken nextToken = peek();
            switch (nextToken.kind) {
                case EOF_TOKEN:
                    return;
                case CLOSE_BRACE_TOKEN:
                    return;
                default:
                    consume();
                    this.errorHandler.reportInvalidNode(nextToken, "invalid token '" + nextToken.text() + "'");
            }
        }
    }

    /**
     * Parse back-tick token.
     *
     * @return Back-tick token
     */
    private STNode parseBacktickToken(ParserRuleContext ctx) {
        STToken token = peek();
        if (token.kind == SyntaxKind.BACKTICK_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ctx);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse table type descriptor.
     * <p>
     * table-type-descriptor := table row-type-parameter [key-constraint]
     * row-type-parameter := type-parameter
     * key-constraint := key-specifier | key-type-constraint
     * key-specifier := key ( [ field-name (, field-name)* ] )
     * key-type-constraint := key type-parameter
     * </p>
     *
     * @return Parsed table type desc node.
     */
    private STNode parseTableTypeDescriptor() {
        STNode tableKeywordToken = parseTableKeyword();
        STNode rowTypeParameterNode = parseRowTypeParameter();
        STNode keyConstraintNode;
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.KEY_KEYWORD) {
            STNode keyKeywordToken = parseKeyKeyword();
            keyConstraintNode = parseKeyConstraint(keyKeywordToken);
        } else {
            keyConstraintNode = STNodeFactory.createEmptyNode();
        }
        return STNodeFactory.createTableTypeDescriptorNode(tableKeywordToken, rowTypeParameterNode, keyConstraintNode);
    }

    /**
     * Parse row type parameter node.
     * <p>
     * row-type-parameter := type-parameter
     * </p>
     *
     * @return Parsed node.
     */
    private STNode parseRowTypeParameter() {
        startContext(ParserRuleContext.ROW_TYPE_PARAM);
        STNode rowTypeParameterNode = parseTypeParameter();
        endContext();
        return rowTypeParameterNode;
    }

    /**
     * Parse type parameter node.
     * <p>
     * type-parameter := < type-descriptor >
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseTypeParameter() {
        STNode ltToken = parseLTToken();
        STNode typeNode = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_ANGLE_BRACKETS);
        STNode gtToken = parseGTToken();
        return STNodeFactory.createTypeParameterNode(ltToken, typeNode, gtToken);
    }

    /**
     * Parse key constraint.
     * <p>
     * key-constraint := key-specifier | key-type-constraint
     * </p>
     *
     * @return Parsed node.
     */
    private STNode parseKeyConstraint(STNode keyKeywordToken) {
        return parseKeyConstraint(peek().kind, keyKeywordToken);
    }

    private STNode parseKeyConstraint(SyntaxKind nextTokenKind, STNode keyKeywordToken) {
        switch (nextTokenKind) {
            case OPEN_PAREN_TOKEN:
                return parseKeySpecifier(keyKeywordToken);
            case LT_TOKEN:
                return parseKeyTypeConstraint(keyKeywordToken);
            default:
                Solution solution = recover(peek(), ParserRuleContext.KEY_CONSTRAINTS_RHS, keyKeywordToken);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }
                return parseKeyConstraint(solution.tokenKind, keyKeywordToken);
        }
    }

    /**
     * Parse key specifier given parsed key keyword token.
     * <p>
     * <code>key-specifier := key ( [ field-name (, field-name)* ] )</code>
     *
     * @return Parsed node
     */
    private STNode parseKeySpecifier(STNode keyKeywordToken) {
        startContext(ParserRuleContext.KEY_SPECIFIER);
        STNode openParenToken = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
        STNode fieldNamesNode = parseFieldNames();
        STNode closeParenToken = parseCloseParenthesis();
        endContext();
        return STNodeFactory.createKeySpecifierNode(keyKeywordToken, openParenToken, fieldNamesNode, closeParenToken);
    }

    /**
     * Parse key type constraint.
     * <p>
     * key-type-constraint := key type-parameter
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseKeyTypeConstraint(STNode keyKeywordToken) {
        STNode typeParameterNode = parseTypeParameter();
        return STNodeFactory.createKeyTypeConstraintNode(keyKeywordToken, typeParameterNode);
    }

    /**
     * Parse function type descriptor.
     * <p>
     * <code>function-type-descriptor := function function-signature</code>
     *
     * @return Function type descriptor node
     */
    private STNode parseFunctionTypeDesc() {
        startContext(ParserRuleContext.FUNC_TYPE_DESC);
        STNode functionKeyword = parseFunctionKeyword();
        STNode signature = parseFuncSignature(true);
        endContext();
        return STNodeFactory.createFunctionTypeDescriptorNode(functionKeyword, signature);
    }

    /**
     * Parse explicit anonymous function expression.
     * <p>
     * <code>explicit-anonymous-function-expr := [annots] function function-signature anon-func-body</code>
     *
     * @param annots Annotations.
     * @return Anonymous function expression node
     */
    private STNode parseExplicitFunctionExpression(STNode annots) {
        startContext(ParserRuleContext.ANON_FUNC_EXPRESSION);
        STNode funcKeyword = parseFunctionKeyword();
        STNode funcSignature = parseFuncSignature(false);
        STNode funcBody = parseAnonFuncBody();
        return STNodeFactory.createExplicitAnonymousFunctionExpressionNode(annots, funcKeyword, funcSignature,
                funcBody);
    }

    /**
     * Parse anonymous function body.
     * <p>
     * <code>anon-func-body := block-function-body | expr-function-body</code>
     *
     * @return
     */
    private STNode parseAnonFuncBody() {
        return parseAnonFuncBody(peek().kind);
    }

    private STNode parseAnonFuncBody(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case OPEN_BRACE_TOKEN:
            case EOF_TOKEN:
                STNode body = parseFunctionBodyBlock(true);
                endContext();
                return body;
            case RIGHT_DOUBLE_ARROW_TOKEN:
                
                
                endContext();
                return parseExpressionFuncBody(true);
            default:
                Solution solution = recover(peek(), ParserRuleContext.ANON_FUNC_BODY);
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }
                return parseAnonFuncBody(solution.tokenKind);
        }
    }

    /**
     * Parse expression function body.
     * <p>
     * <code>expr-function-body := => expression</code>
     *
     * @return Expression function body node
     */
    private STNode parseExpressionFuncBody(boolean isAnon) {
        STNode rightDoubleArrow = parseDoubleRightArrow();
        STNode expression = parseExpression();

        STNode semiColon;
        if (isAnon) {
            semiColon = STNodeFactory.createEmptyNode();
        } else {
            semiColon = parseSemicolon();
        }
        return STNodeFactory.createExpressionFunctionBodyNode(rightDoubleArrow, expression, semiColon);
    }

    /**
     * Parse '=>' token.
     *
     * @return Double right arrow token
     */
    private STNode parseDoubleRightArrow() {
        STToken token = peek();
        if (token.kind == SyntaxKind.RIGHT_DOUBLE_ARROW_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.EXPR_FUNC_BODY_START);
            return sol.recoveredNode;
        }
    }

    private STNode parseImplicitAnonFunc(STNode params) {
        switch (params.kind) {
            case SIMPLE_NAME_REFERENCE:
            case INFER_PARAM_LIST:
                break;
            case BRACED_EXPRESSION:
                params = getAnonFuncParam((STBracedExpressionNode) params);
                break;
            default:
                this.errorHandler.reportInvalidNode(null, "lhs must be an identifier or a param list");
        }
        STNode rightDoubleArrow = parseDoubleRightArrow();
        STNode expression = parseExpression();
        return STNodeFactory.createImplicitAnonymousFunctionExpressionNode(params, rightDoubleArrow, expression);
    }

    /**
     * Create a new anon-func-param node from a braced expression.
     *
     * @param params Braced expression
     * @return Anon-func param node
     */
    private STNode getAnonFuncParam(STBracedExpressionNode params) {
        List<STNode> paramList = new ArrayList<>();
        paramList.add(params.expression);
        return STNodeFactory.createImplicitAnonymousFunctionParameters(params.openParen,
                STNodeFactory.createNodeList(paramList), params.closeParen);
    }

    /**
     * Parse implicit anon function expression.
     *
     * @param openParen Open parenthesis token
     * @param firstParam First parameter
     * @return Implicit anon function expression node
     */
    private STNode parseImplicitAnonFunc(STNode openParen, STNode firstParam) {
        List<STNode> paramList = new ArrayList<>();
        paramList.add(firstParam);

        
        STToken nextToken = peek();
        STNode paramEnd;
        STNode param;
        while (!isEndOfAnonFuncParametersList(nextToken.kind)) {
            paramEnd = parseImplicitAnonFuncParamEnd(nextToken.kind);
            if (paramEnd == null) {
                break;
            }

            paramList.add(paramEnd);
            param = parseIdentifier(ParserRuleContext.IMPLICIT_ANON_FUNC_PARAM);
            paramList.add(param);
            nextToken = peek();
        }

        STNode params = STNodeFactory.createNodeList(paramList);
        STNode closeParen = parseCloseParenthesis();
        endContext(); 

        STNode inferedParams = STNodeFactory.createImplicitAnonymousFunctionParameters(openParen, params, closeParen);
        return parseImplicitAnonFunc(inferedParams);
    }

    private STNode parseImplicitAnonFuncParamEnd() {
        return parseImplicitAnonFuncParamEnd(peek().kind);
    }

    private STNode parseImplicitAnonFuncParamEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_PAREN_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.ANON_FUNC_PARAM_RHS);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseImplicitAnonFuncParamEnd(solution.tokenKind);
        }
    }

    private boolean isEndOfAnonFuncParametersList(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACE_TOKEN:
            case CLOSE_PAREN_TOKEN:
            case CLOSE_BRACKET_TOKEN:
            case SEMICOLON_TOKEN:
            case RETURNS_KEYWORD:
            case TYPE_KEYWORD:
            case LISTENER_KEYWORD:
            case IF_KEYWORD:
            case WHILE_KEYWORD:
            case OPEN_BRACE_TOKEN:
            case RIGHT_DOUBLE_ARROW_TOKEN:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse tuple type descriptor.
     * <p>
     * <code>tuple-type-descriptor := [ tuple-member-type-descriptors ]
     * <br/><br/>
     * tuple-member-type-descriptors := member-type-descriptor (, member-type-descriptor)* [, tuple-rest-descriptor]
     *                                     | [ tuple-rest-descriptor ]
     * <br/><br/>
     * tuple-rest-descriptor := type-descriptor ...
     * </code>
     *
     * @return
     */
    private STNode parseTupleTypeDesc() {
        STNode openBracket = parseOpenBracket();
        startContext(ParserRuleContext.TYPE_DESC_IN_TUPLE);
        STNode memberTypeDesc = parseTupleMemberTypeDescList();
        STNode closeBracket = parseCloseBracket();
        endContext();

        
        openBracket =
                addDiagnosticIfListEmpty(memberTypeDesc, openBracket, DiagnosticErrorCode.ERROR_MISSING_TYPE_DESC);

        return STNodeFactory.createTupleTypeDescriptorNode(openBracket, memberTypeDesc, closeBracket);
    }

    /**
     * Parse tuple member type descriptors.
     *
     * @return Parsed node
     */
    private STNode parseTupleMemberTypeDescList() {
        List<STNode> typeDescList = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfTypeList(nextToken.kind)) {
            return STNodeFactory.createEmptyNodeList();
        }

        
        STNode typeDesc = parseTypeDescriptorInternal(ParserRuleContext.TYPE_DESC_IN_TUPLE);

        return parseTupleTypeMembers(typeDesc, typeDescList);
    }

    private STNode parseTupleTypeMembers(STNode typeDesc, List<STNode> typeDescList) {
        STToken nextToken;
        
        nextToken = peek();
        STNode tupleMemberRhs;
        while (!isEndOfTypeList(nextToken.kind)) {
            tupleMemberRhs = parseTupleMemberRhs(nextToken.kind);
            if (tupleMemberRhs == null) {
                break;
            }
            if (tupleMemberRhs.kind == SyntaxKind.ELLIPSIS_TOKEN) {
                typeDesc = STNodeFactory.createRestDescriptorNode(typeDesc, tupleMemberRhs);
                break;
            }
            typeDescList.add(typeDesc);
            typeDescList.add(tupleMemberRhs);
            typeDesc = parseTypeDescriptorInternal(ParserRuleContext.TYPE_DESC_IN_TUPLE);
            nextToken = peek();
        }

        typeDescList.add(typeDesc);

        return STNodeFactory.createNodeList(typeDescList);
    }

    private STNode parseTupleMemberRhs() {
        return parseTupleMemberRhs(peek().kind);
    }

    private STNode parseTupleMemberRhs(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACKET_TOKEN:
                return null;
            case ELLIPSIS_TOKEN:
                return parseEllipsis();
            default:
                Solution solution = recover(peek(), ParserRuleContext.TYPE_DESC_IN_TUPLE_RHS);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseTupleMemberRhs(solution.tokenKind);
        }
    }

    private boolean isEndOfTypeList(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case CLOSE_BRACKET_TOKEN:
            case CLOSE_BRACE_TOKEN:
            case CLOSE_PAREN_TOKEN:
            case EOF_TOKEN:
            case EQUAL_TOKEN:
            case OPEN_BRACE_TOKEN:
            case SEMICOLON_TOKEN:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse table constructor or query expression.
     * <p>
     * <code>
     * table-constructor-or-query-expr := table-constructor-expr | query-expr
     * <br/>
     * table-constructor-expr := table [key-specifier] [ [row-list] ]
     * <br/>
     * query-expr := [query-construct-type] query-pipeline select-clause
     * <br/>
     * query-construct-type := table key-specifier | stream
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseTableConstructorOrQuery(boolean isRhsExpr) {
        startContext(ParserRuleContext.TABLE_CONSTRUCTOR_OR_QUERY_EXPRESSION);
        STNode tableOrQueryExpr = parseTableConstructorOrQuery(peek().kind, isRhsExpr);
        endContext();
        return tableOrQueryExpr;
    }

    private STNode parseTableConstructorOrQuery(SyntaxKind nextTokenKind, boolean isRhsExpr) {
        STNode queryConstructType;
        switch (nextTokenKind) {
            case FROM_KEYWORD:
                queryConstructType = STNodeFactory.createEmptyNode();
                return parseQueryExprRhs(queryConstructType, isRhsExpr);
            case STREAM_KEYWORD:
                queryConstructType = parseStreamKeyword();
                return parseQueryExprRhs(queryConstructType, isRhsExpr);
            case TABLE_KEYWORD:
                STNode tableKeyword = parseTableKeyword();
                return parseTableConstructorOrQuery(tableKeyword, isRhsExpr);
            default:
                Solution solution = recover(peek(), ParserRuleContext.TABLE_CONSTRUCTOR_OR_QUERY_START, isRhsExpr);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseTableConstructorOrQuery(solution.tokenKind, isRhsExpr);
        }

    }

    private STNode parseTableConstructorOrQuery(STNode tableKeyword, boolean isRhsExpr) {
        return parseTableConstructorOrQuery(peek().kind, tableKeyword, isRhsExpr);
    }

    private STNode parseTableConstructorOrQuery(SyntaxKind nextTokenKind, STNode tableKeyword, boolean isRhsExpr) {
        STNode keySpecifier;
        switch (nextTokenKind) {
            case OPEN_BRACKET_TOKEN:
                keySpecifier = STNodeFactory.createEmptyNode();
                return parseTableConstructorExprRhs(tableKeyword, keySpecifier);
            case KEY_KEYWORD:
                keySpecifier = parseKeySpecifier();
                return parseTableConstructorOrQueryRhs(tableKeyword, keySpecifier, isRhsExpr);
            default:
                Solution solution = recover(peek(), ParserRuleContext.TABLE_KEYWORD_RHS, tableKeyword, isRhsExpr);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseTableConstructorOrQuery(solution.tokenKind, tableKeyword, isRhsExpr);
        }
    }

    private STNode parseTableConstructorOrQueryRhs(STNode tableKeyword, STNode keySpecifier, boolean isRhsExpr) {
        return parseTableConstructorOrQueryRhs(peek().kind, tableKeyword, keySpecifier, isRhsExpr);
    }

    private STNode parseTableConstructorOrQueryRhs(SyntaxKind nextTokenKind, STNode tableKeyword, STNode keySpecifier,
                                                   boolean isRhsExpr) {
        switch (nextTokenKind) {
            case FROM_KEYWORD:
                return parseQueryExprRhs(parseQueryConstructType(tableKeyword, keySpecifier), isRhsExpr);
            case OPEN_BRACKET_TOKEN:
                return parseTableConstructorExprRhs(tableKeyword, keySpecifier);
            default:
                Solution solution = recover(peek(), ParserRuleContext.TABLE_CONSTRUCTOR_OR_QUERY_RHS, tableKeyword,
                        keySpecifier, isRhsExpr);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseTableConstructorOrQueryRhs(solution.tokenKind, tableKeyword, keySpecifier, isRhsExpr);
        }
    }

    /**
     * Parse query construct type.
     * <p>
     * <code>query-construct-type := table key-specifier</code>
     *
     * @return Parsed node
     */
    private STNode parseQueryConstructType(STNode tableKeyword, STNode keySpecifier) {
        return STNodeFactory.createQueryConstructTypeNode(tableKeyword, keySpecifier);
    }

    /**
     * Parse query expression.
     * <p>
     * <code>
     * query-expr-rhs := query-pipeline select-clause
     * <br/>
     * query-pipeline := from-clause intermediate-clause*
     * </code>
     *
     * @param queryConstructType queryConstructType that precedes this rhs
     * @return Parsed node
     */
    private STNode parseQueryExprRhs(STNode queryConstructType, boolean isRhsExpr) {
        switchContext(ParserRuleContext.QUERY_EXPRESSION);
        STNode fromClause = parseFromClause(isRhsExpr);

        List<STNode> clauses = new ArrayList<>();
        STNode intermediateClause;
        STNode selectClause = null;
        while (!isEndOfIntermediateClause(peek().kind)) {
            intermediateClause = parseIntermediateClause(isRhsExpr);
            if (intermediateClause == null) {
                break;
            }

            
            if (selectClause != null) {
                
                this.errorHandler.reportInvalidNode(null, "extra clauses after select clause");
                break;
            }

            if (intermediateClause.kind == SyntaxKind.SELECT_CLAUSE) {
                selectClause = intermediateClause;
            } else {
                clauses.add(intermediateClause);
            }
        }

        if (peek().kind == SyntaxKind.DO_KEYWORD) {
            STNode intermediateClauses = STNodeFactory.createNodeList(clauses);
            STNode queryPipeline = STNodeFactory.createQueryPipelineNode(fromClause, intermediateClauses);
            return parseQueryAction(queryPipeline, selectClause);
        }

        if (selectClause == null) {
            STNode selectKeyword = errorHandler.createMissingToken(SyntaxKind.SELECT_KEYWORD);
            STNode expr = errorHandler.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN);
            selectClause = STNodeFactory.createSelectClauseNode(selectKeyword, expr);

            
            
            if (clauses.isEmpty()) {
                fromClause = errorHandler.addDiagnostics(fromClause, DiagnosticErrorCode.ERROR_MISSING_SELECT_CLAUSE);
            } else {
                int lastIndex = clauses.size() - 1;
                STNode intClauseWithDiagnostic = errorHandler.addDiagnostics(clauses.get(lastIndex),
                        DiagnosticErrorCode.ERROR_MISSING_SELECT_CLAUSE);
                clauses.set(lastIndex, intClauseWithDiagnostic);
            }
        }

        STNode intermediateClauses = STNodeFactory.createNodeList(clauses);
        STNode queryPipeline = STNodeFactory.createQueryPipelineNode(fromClause, intermediateClauses);
        return STNodeFactory.createQueryExpressionNode(queryConstructType, queryPipeline, selectClause);
    }

    /**
     * Parse an intermediate clause.
     * <p>
     * <code>
     * intermediate-clause := from-clause | where-clause | let-clause
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseIntermediateClause(boolean isRhsExpr) {
        return parseIntermediateClause(peek().kind, isRhsExpr);
    }

    private STNode parseIntermediateClause(SyntaxKind nextTokenKind, boolean isRhsExpr) {
        switch (nextTokenKind) {
            case FROM_KEYWORD:
                return parseFromClause(isRhsExpr);
            case WHERE_KEYWORD:
                return parseWhereClause(isRhsExpr);
            case LET_KEYWORD:
                return parseLetClause(isRhsExpr);
            case SELECT_KEYWORD:
                return parseSelectClause(isRhsExpr);
            case DO_KEYWORD:
            case SEMICOLON_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.QUERY_PIPELINE_RHS, isRhsExpr);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseIntermediateClause(solution.tokenKind, isRhsExpr);
        }
    }

    private boolean isEndOfIntermediateClause(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case CLOSE_BRACE_TOKEN:
            case CLOSE_PAREN_TOKEN:
            case CLOSE_BRACKET_TOKEN:
            case OPEN_BRACE_TOKEN:
            case SEMICOLON_TOKEN:
            case PUBLIC_KEYWORD:
            case FUNCTION_KEYWORD:
            case EOF_TOKEN:
            case RESOURCE_KEYWORD:
            case LISTENER_KEYWORD:
            case DOCUMENTATION_LINE:
            case PRIVATE_KEYWORD:
            case RETURNS_KEYWORD:
            case SERVICE_KEYWORD:
            case TYPE_KEYWORD:
            case CONST_KEYWORD:
            case FINAL_KEYWORD:
            case DO_KEYWORD:
                return true;
            default:
                return isValidExprRhsStart(tokenKind);
        }
    }

    /**
     * Parse from clause.
     * <p>
     * <code>from-clause := from typed-binding-pattern in expression</code>
     *
     * @return Parsed node
     */
    private STNode parseFromClause(boolean isRhsExpr) {
        STNode fromKeyword = parseFromKeyword();
        STNode typedBindingPattern = parseTypedBindingPattern(ParserRuleContext.FROM_CLAUSE);
        STNode inKeyword = parseInKeyword();

        
        
        STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
        return STNodeFactory.createFromClauseNode(fromKeyword, typedBindingPattern, inKeyword, expression);
    }

    /**
     * Parse from-keyword.
     *
     * @return From-keyword node
     */
    private STNode parseFromKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FROM_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FROM_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse where clause.
     * <p>
     * <code>where-clause := where expression</code>
     *
     * @return Parsed node
     */
    private STNode parseWhereClause(boolean isRhsExpr) {
        STNode whereKeyword = parseWhereKeyword();

        
        
        STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
        return STNodeFactory.createWhereClauseNode(whereKeyword, expression);
    }

    /**
     * Parse where-keyword.
     *
     * @return Where-keyword node
     */
    private STNode parseWhereKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.WHERE_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.WHERE_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse let clause.
     * <p>
     * <code>let-clause := let let-var-decl [, let-var-decl]* </code>
     *
     * @return Parsed node
     */
    private STNode parseLetClause(boolean isRhsExpr) {
        STNode letKeyword = parseLetKeyword();
        STNode letVarDeclarations = parseLetVarDeclarations(ParserRuleContext.LET_CLAUSE_LET_VAR_DECL, isRhsExpr);

        
        letKeyword = addDiagnosticIfListEmpty(letVarDeclarations, letKeyword,
                DiagnosticErrorCode.ERROR_MISSING_LET_VARIABLE_DECLARATION);

        return STNodeFactory.createLetClauseNode(letKeyword, letVarDeclarations);
    }

    /**
     * Parse select clause.
     * <p>
     * <code>select-clause := select expression</code>
     *
     * @return Parsed node
     */
    private STNode parseSelectClause(boolean isRhsExpr) {
        STNode selectKeyword = parseSelectKeyword();

        
        
        STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
        return STNodeFactory.createSelectClauseNode(selectKeyword, expression);
    }

    /**
     * Parse select-keyword.
     *
     * @return Select-keyword node
     */
    private STNode parseSelectKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.SELECT_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.SELECT_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse start action.
     * <p>
     * <code>start-action := [annots] start (function-call-expr|method-call-expr|remote-method-call-action)</code>
     *
     * @return Start action node
     */
    private STNode parseStartAction(STNode annots) {
        STNode startKeyword = parseStartKeyword();
        STNode expr = parseActionOrExpression();
        validateExprInStartAction(expr);
        return STNodeFactory.createStartActionNode(getAnnotations(annots), startKeyword, expr);
    }

    /**
     * Parse start keyword.
     *
     * @return Start keyword node
     */
    private STNode parseStartKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.START_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.START_KEYWORD);
            return sol.recoveredNode;
        }
    }

    private void validateExprInStartAction(STNode expression) {
        switch (expression.kind) {
            case FUNCTION_CALL:
            case METHOD_CALL:
            case REMOTE_METHOD_CALL_ACTION:
                break;
            default:
                if (isMissingNode(expression)) {
                    break;
                }

                this.errorHandler.reportInvalidNode(null, "expression followed by the start keyword must be a " +
                        "func-call, a method-call or a remote-method-call");
                break;
        }
    }

    /**
     * Parse flush action.
     * <p>
     * <code>flush-action := flush [peer-worker]</code>
     *
     * @return flush action node
     */
    private STNode parseFlushAction() {
        STNode flushKeyword = parseFlushKeyword();
        STNode peerWorker = parseOptionalPeerWorkerName();
        return STNodeFactory.createFlushActionNode(flushKeyword, peerWorker);
    }

    /**
     * Parse flush keyword.
     *
     * @return flush keyword node
     */
    private STNode parseFlushKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.FLUSH_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.FLUSH_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse peer worker.
     * <p>
     * <code>peer-worker := worker-name | default</code>
     *
     * @return peer worker name node
     */
    private STNode parseOptionalPeerWorkerName() {
        STToken token = peek();
        switch (token.kind) {
            case IDENTIFIER_TOKEN:
            case DEFAULT_KEYWORD:
                return STNodeFactory.createSimpleNameReferenceNode(consume());
            default:
                return STNodeFactory.createEmptyNode();
        }
    }

    /**
     * Parse intersection type descriptor.
     * <p>
     * intersection-type-descriptor := type-descriptor & type-descriptor
     * </p>
     *
     * @return Parsed node
     */
    private STNode parseIntersectionTypeDescriptor(STNode leftTypeDesc, ParserRuleContext context) {
        
        STNode bitwiseAndToken = consume();
        STNode rightTypeDesc = parseTypeDescriptor(context);
        return STNodeFactory.createIntersectionTypeDescriptorNode(leftTypeDesc, bitwiseAndToken, rightTypeDesc);
    }

    /**
     * Parse singleton type descriptor.
     * <p>
     * singleton-type-descriptor := simple-const-expr
     * simple-const-expr :=
     * nil-literal
     * | boolean-literal
     * | [Sign] int-literal
     * | [Sign] floating-point-literal
     * | string-literal
     * | constant-reference-expr
     * </p>
     */
    private STNode parseSingletonTypeDesc() {
        STNode simpleContExpr = parseConstExpr();
        return STNodeFactory.createSingletonTypeDescriptorNode(simpleContExpr);
    }

    private STNode parseSignedIntOrFloat() {
        STNode operator = parseUnaryOperator();
        STNode literal;
        STToken nextToken = peek();
        switch (nextToken.kind) {
            case HEX_INTEGER_LITERAL:
            case DECIMAL_FLOATING_POINT_LITERAL:
            case HEX_FLOATING_POINT_LITERAL:
                literal = consume();
                break;
            default: 
                literal = parseDecimalIntLiteral(ParserRuleContext.DECIMAL_INTEGER_LITERAL);
        }
        return STNodeFactory.createUnaryExpressionNode(operator, literal);
    }

    private boolean isSingletonTypeDescStart(SyntaxKind tokenKind, boolean inTypeDescCtx) {
        STToken nextNextToken = getNextNextToken(tokenKind);
        switch (tokenKind) {
            case STRING_LITERAL:
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case DECIMAL_FLOATING_POINT_LITERAL:
            case HEX_FLOATING_POINT_LITERAL:
            case TRUE_KEYWORD:
            case FALSE_KEYWORD:
            case NULL_KEYWORD:
                if (inTypeDescCtx || isValidTypeDescRHSOutSideTypeDescCtx(nextNextToken)) {
                    return true;
                }
                return false;
            case PLUS_TOKEN:
            case MINUS_TOKEN:
                return isIntOrFloat(nextNextToken);
            default:
                return false;
        }
    }

    static boolean isIntOrFloat(STToken token) {
        switch (token.kind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case DECIMAL_FLOATING_POINT_LITERAL:
            case HEX_FLOATING_POINT_LITERAL:
                return true;
            default:
                return false;
        }
    }

    private boolean isValidTypeDescRHSOutSideTypeDescCtx(STToken token) {
        switch (token.kind) {
            case IDENTIFIER_TOKEN:
            case QUESTION_MARK_TOKEN:
            case OPEN_PAREN_TOKEN:
            case OPEN_BRACKET_TOKEN:
            case PIPE_TOKEN:
            case BITWISE_AND_TOKEN:
            case OPEN_BRACE_TOKEN:
            case ERROR_KEYWORD:
                return true;
            default:
                return false;
        }
    }

    /**
     * Check whether the parser reached to a valid expression start.
     *
     * @param nextTokenKind Kind of the next immediate token.
     * @param nextTokenIndex Index to the next token.
     * @return <code>true</code> if this is a start of a valid expression. <code>false</code> otherwise
     */
    private boolean isValidExpressionStart(SyntaxKind nextTokenKind, int nextTokenIndex) {
        switch (nextTokenKind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case STRING_LITERAL:
            case NULL_KEYWORD:
            case TRUE_KEYWORD:
            case FALSE_KEYWORD:
            case DECIMAL_FLOATING_POINT_LITERAL:
            case HEX_FLOATING_POINT_LITERAL:
            case IDENTIFIER_TOKEN:
                return isValidExprRhsStart(peek(nextTokenIndex + 1).kind);

            case OPEN_PAREN_TOKEN:
            case CHECK_KEYWORD:
            case CHECKPANIC_KEYWORD:
            case OPEN_BRACE_TOKEN:
            case TYPEOF_KEYWORD:
            case NEGATION_TOKEN:
            case EXCLAMATION_MARK_TOKEN:
            case TRAP_KEYWORD:
            case OPEN_BRACKET_TOKEN:
            case LT_TOKEN:
            case TABLE_KEYWORD:
            case STREAM_KEYWORD:
            case FROM_KEYWORD:
            case ERROR_KEYWORD:
            case LET_KEYWORD:
            case BACKTICK_TOKEN:
            case XML_KEYWORD:
            case STRING_KEYWORD:
            case FUNCTION_KEYWORD:
            case NEW_KEYWORD:
            case LEFT_ARROW_TOKEN:
            case SERVICE_KEYWORD:
                return true;
            case PLUS_TOKEN:
            case MINUS_TOKEN:
                return isValidExpressionStart(peek(nextTokenIndex).kind, nextTokenIndex + 1);

            
            case START_KEYWORD:
            case FLUSH_KEYWORD:
            case WAIT_KEYWORD:
            default:
                return false;
        }
    }

    /**
     * Parse sync send action.
     * <p>
     * <code>sync-send-action := expression ->> peer-worker</code>
     *
     * @param expression LHS expression of the sync send action
     * @return Sync send action node
     */
    private STNode parseSyncSendAction(STNode expression) {
        STNode syncSendToken = parseSyncSendToken();
        STNode peerWorker = parsePeerWorkerName();
        return STNodeFactory.createSyncSendActionNode(expression, syncSendToken, peerWorker);
    }

    /**
     * Parse peer worker.
     * <p>
     * <code>peer-worker := worker-name | default</code>
     *
     * @return peer worker name node
     */
    private STNode parsePeerWorkerName() {
        STToken token = peek();
        switch (token.kind) {
            case IDENTIFIER_TOKEN:
            case DEFAULT_KEYWORD:
                return STNodeFactory.createSimpleNameReferenceNode(consume());
            default:
                Solution sol = recover(token, ParserRuleContext.PEER_WORKER_NAME);
                return sol.recoveredNode;
        }
    }

    /**
     * Parse sync send token.
     * <p>
     * <code>sync-send-token :=  ->> </code>
     *
     * @return sync send token
     */
    private STNode parseSyncSendToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.SYNC_SEND_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.SYNC_SEND_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse receive action.
     * <p>
     * <code>receive-action := single-receive-action | multiple-receive-action</code>
     *
     * @return Receive action
     */
    private STNode parseReceiveAction() {
        STNode leftArrow = parseLeftArrowToken();
        STNode receiveWorkers = parseReceiveWorkers();
        return STNodeFactory.createReceiveActionNode(leftArrow, receiveWorkers);
    }

    private STNode parseReceiveWorkers() {
        return parseReceiveWorkers(peek().kind);
    }

    private STNode parseReceiveWorkers(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case DEFAULT_KEYWORD:
            case IDENTIFIER_TOKEN:
                return parsePeerWorkerName();
            case OPEN_BRACE_TOKEN:
                return parseMultipleReceiveWorkers();
            default:
                Solution solution = recover(peek(), ParserRuleContext.RECEIVE_WORKERS);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseReceiveWorkers(solution.tokenKind);
        }
    }

    /**
     * Parse multiple worker receivers.
     * <p>
     * <code>{ receive-field (, receive-field)* }</code>
     *
     * @return Multiple worker receiver node
     */
    private STNode parseMultipleReceiveWorkers() {
        startContext(ParserRuleContext.MULTI_RECEIVE_WORKERS);
        STNode openBrace = parseOpenBrace();
        STNode receiveFields = parseReceiveFields();
        STNode closeBrace = parseCloseBrace();
        endContext();
        return STNodeFactory.createReceiveFieldsNode(openBrace, receiveFields, closeBrace);
    }

    private STNode parseReceiveFields() {
        List<STNode> receiveFields = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfReceiveFields(nextToken.kind)) {
            this.errorHandler.reportMissingTokenError("missing receive field");
            return STNodeFactory.createNodeList(new ArrayList<>());
        }

        
        STNode receiveField = parseReceiveField();
        receiveFields.add(receiveField);

        
        nextToken = peek();
        STNode recieveFieldEnd;
        while (!isEndOfReceiveFields(nextToken.kind)) {
            recieveFieldEnd = parseReceiveFieldEnd(nextToken.kind);
            if (recieveFieldEnd == null) {
                break;
            }

            receiveFields.add(recieveFieldEnd);
            receiveField = parseReceiveField();
            receiveFields.add(receiveField);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(receiveFields);
    }

    private boolean isEndOfReceiveFields(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACKET_TOKEN:
                return true;
            default:
                return false;
        }
    }

    private STNode parseReceiveFieldEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACE_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.RECEIVE_FIELD_END);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseReceiveFieldEnd(solution.tokenKind);
        }
    }

    private STNode parseReceiveField() {
        return parseReceiveField(peek().kind);
    }

    /**
     * Parse receive field.
     * <p>
     * <code>receive-field := peer-worker | field-name : peer-worker</code>
     *
     * @param nextTokenKind Kind of the next token
     * @return Receiver field node
     */
    private STNode parseReceiveField(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case DEFAULT_KEYWORD:
                return parseDefaultKeyword();
            case IDENTIFIER_TOKEN:
                STNode identifier = parseIdentifier(ParserRuleContext.RECEIVE_FIELD_NAME);
                return createQualifiedReceiveField(identifier);
            default:
                Solution solution = recover(peek(), ParserRuleContext.RECEIVE_FIELD);

                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                if (solution.tokenKind == SyntaxKind.IDENTIFIER_TOKEN) {
                    return createQualifiedReceiveField(solution.recoveredNode);
                }

                return solution.recoveredNode;
        }
    }

    private STNode createQualifiedReceiveField(STNode identifier) {
        if (peek().kind != SyntaxKind.COLON_TOKEN) {
            return identifier;
        }

        STNode colon = parseColon();
        STNode peerWorker = parsePeerWorkerName();
        return STNodeFactory.createQualifiedNameReferenceNode(identifier, colon, peerWorker);
    }

    /**
     *
     * Parse left arrow (<-) token.
     *
     * @return left arrow token
     */
    private STNode parseLeftArrowToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.LEFT_ARROW_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.LEFT_ARROW_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse signed right shift token (>>).
     *
     * @return Parsed node
     */
    private STNode parseSignedRightShiftToken() {
        STNode openGTToken = parseGTToken();
        validateRightShiftOperatorWS(openGTToken);

        STNode endLGToken = parseGTToken();
        return STNodeFactory.createDoubleGTTokenNode(openGTToken, endLGToken);
    }

    /**
     * Parse unsigned right shift token (>>>).
     *
     * @return Parsed node
     */
    private STNode parseUnsignedRightShiftToken() {
        STNode openGTToken = parseGTToken();
        validateRightShiftOperatorWS(openGTToken);

        STNode middleGTToken = parseGTToken();
        validateRightShiftOperatorWS(middleGTToken);

        STNode endLGToken = parseGTToken();
        return STNodeFactory.createTrippleGTTokenNode(openGTToken, middleGTToken, endLGToken);
    }

    /**
     * Validate the whitespace between '>' tokens of right shift operators.
     *
     * @param node Preceding node
     */
    private void validateRightShiftOperatorWS(STNode node) {
        int diff = node.widthWithTrailingMinutiae() - node.width();
        if (diff > 0) {
            this.errorHandler.reportMissingTokenError("no whitespaces allowed between >>");
        }
    }

    /**
     * Parse wait action.
     * <p>
     * <code>wait-action := single-wait-action | multiple-wait-action | alternate-wait-action </code>
     *
     * @return Wait action node
     */
    private STNode parseWaitAction() {
        STNode waitKeyword = parseWaitKeyword();
        if (peek().kind == SyntaxKind.OPEN_BRACE_TOKEN) {
            return parseMultiWaitAction(waitKeyword);
        }

        return parseSingleOrAlternateWaitAction(waitKeyword);
    }

    /**
     * Parse wait keyword.
     *
     * @return wait keyword
     */
    private STNode parseWaitKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.WAIT_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.WAIT_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse single or alternate wait actions.
     * <p>
     * <code>
     * alternate-or-single-wait-action := wait wait-future-expr (| wait-future-expr)+
     * <br/>
     * wait-future-expr := expression but not mapping-constructor-expr
     * </code>
     *
     * @param waitKeyword wait keyword
     * @return Single or alternate wait action node
     */
    private STNode parseSingleOrAlternateWaitAction(STNode waitKeyword) {
        startContext(ParserRuleContext.ALTERNATE_WAIT_EXPRS);
        List<STNode> waitFutureExprList = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfWaitFutureExprList(nextToken.kind)) {
            this.errorHandler.reportMissingTokenError("missing wait field");
            endContext();
            STNode waitFutureExprs = STNodeFactory.createNodeList(waitFutureExprList);
            return STNodeFactory.createWaitActionNode(waitKeyword, waitFutureExprs);
        }

        
        STNode waitField = parseWaitFutureExpr();
        waitFutureExprList.add(waitField);

        
        nextToken = peek();
        STNode waitFutureExprEnd;
        while (!isEndOfWaitFutureExprList(nextToken.kind)) {
            waitFutureExprEnd = parseWaitFutureExprEnd(nextToken.kind, 1);
            if (waitFutureExprEnd == null) {
                break;
            }

            waitFutureExprList.add(waitFutureExprEnd);
            waitField = parseWaitFutureExpr();
            waitFutureExprList.add(waitField);
            nextToken = peek();
        }

        
        
        endContext();
        return STNodeFactory.createWaitActionNode(waitKeyword, waitFutureExprList.get(0));
    }

    private boolean isEndOfWaitFutureExprList(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACE_TOKEN:
            case SEMICOLON_TOKEN:
                return true;
            default:
                return false;
        }
    }

    private STNode parseWaitFutureExpr() {
        STNode waitFutureExpr = parseExpression();
        if (waitFutureExpr.kind == SyntaxKind.MAPPING_CONSTRUCTOR) {
            this.errorHandler.reportInvalidNode(null, "mapping constructor expression cannot use as å wait expression");
        }
        return waitFutureExpr;
    }

    private STNode parseWaitFutureExprEnd(int nextTokenIndex) {
        return parseWaitFutureExprEnd(peek().kind, 1);
    }

    private STNode parseWaitFutureExprEnd(SyntaxKind nextTokenKind, int nextTokenIndex) {
        switch (nextTokenKind) {
            case PIPE_TOKEN:
                return parsePipeToken();
            default:
                if (isEndOfWaitFutureExprList(nextTokenKind) ||
                        !isValidExpressionStart(nextTokenKind, nextTokenIndex)) {
                    return null;
                }

                Solution solution = recover(peek(), ParserRuleContext.WAIT_FUTURE_EXPR_END, nextTokenIndex);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                
                return parseWaitFutureExprEnd(solution.tokenKind, 0);
        }
    }

    /**
     * Parse multiple wait action.
     * <p>
     * <code>multiple-wait-action := wait { wait-field (, wait-field)* }</code>
     *
     * @param waitKeyword Wait keyword
     * @return Multiple wait action node
     */
    private STNode parseMultiWaitAction(STNode waitKeyword) {
        startContext(ParserRuleContext.MULTI_WAIT_FIELDS);
        STNode openBrace = parseOpenBrace();
        STNode waitFields = parseWaitFields();
        STNode closeBrace = parseCloseBrace();
        endContext();

        STNode waitFieldsNode = STNodeFactory.createWaitFieldsListNode(openBrace, waitFields, closeBrace);
        return STNodeFactory.createWaitActionNode(waitKeyword, waitFieldsNode);
    }

    private STNode parseWaitFields() {
        List<STNode> waitFields = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfReceiveFields(nextToken.kind)) {
            this.errorHandler.reportMissingTokenError("missing wait field");
            return STNodeFactory.createEmptyNodeList();
        }

        
        STNode waitField = parseWaitField();
        waitFields.add(waitField);

        
        nextToken = peek();
        STNode waitFieldEnd;
        while (!isEndOfReceiveFields(nextToken.kind)) {
            waitFieldEnd = parseWaitFieldEnd(nextToken.kind);
            if (waitFieldEnd == null) {
                break;
            }

            waitFields.add(waitFieldEnd);
            waitField = parseWaitField();
            waitFields.add(waitField);
            nextToken = peek();
        }

        return STNodeFactory.createNodeList(waitFields);
    }

    private STNode parseWaitFieldEnd() {
        return parseWaitFieldEnd(peek().kind);
    }

    private STNode parseWaitFieldEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACE_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.WAIT_FIELD_END);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseWaitFieldEnd(solution.tokenKind);
        }
    }

    private STNode parseWaitField() {
        return parseWaitField(peek().kind);
    }

    /**
     * Parse wait field.
     * <p>
     * <code>wait-field := variable-name | field-name : wait-future-expr</code>
     *
     * @param nextTokenKind Kind of the next token
     * @return Receiver field node
     */
    private STNode parseWaitField(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN:
                STNode identifier = parseIdentifier(ParserRuleContext.WAIT_FIELD_NAME);
                return createQualifiedWaitField(identifier);
            default:
                Solution solution = recover(peek(), ParserRuleContext.WAIT_FIELD_NAME);

                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseWaitField(solution.tokenKind);
        }
    }

    private STNode createQualifiedWaitField(STNode identifier) {
        if (peek().kind != SyntaxKind.COLON_TOKEN) {
            return identifier;
        }

        STNode colon = parseColon();
        STNode waitFutureExpr = parseWaitFutureExpr();
        return STNodeFactory.createWaitFieldNode(identifier, colon, waitFutureExpr);
    }

    /**
     * Parse annot access expression.
     * <p>
     * <code>
     * annot-access-expr := expression .@ annot-tag-reference
     * <br/>
     * annot-tag-reference := qualified-identifier | identifier
     * </code>
     *
     * @param lhsExpr Preceding expression of the annot access access
     * @return Parsed node
     */
    private STNode parseAnnotAccessExpression(STNode lhsExpr) {
        STNode annotAccessToken = parseAnnotChainingToken();
        STNode annotTagReference = parseFieldAccessIdentifier();
        return STNodeFactory.createAnnotAccessExpressionNode(lhsExpr, annotAccessToken, annotTagReference);
    }

    /**
     * Parse annot-chaining-token.
     *
     * @return Parsed node
     */
    private STNode parseAnnotChainingToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ANNOT_CHAINING_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ANNOT_CHAINING_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse field access identifier.
     * <p>
     * <code>field-access-identifier := qualified-identifier | identifier</code>
     *
     * @return Parsed node
     */
    private STNode parseFieldAccessIdentifier() {
        return parseQualifiedIdentifier(ParserRuleContext.FIELD_ACCESS_IDENTIFIER);
    }

    /**
     * Parse query action.
     * <p>
     * <code>query-action := query-pipeline do-clause
     * <br/>
     * do-clause := do block-stmt
     * </code>
     *
     * @param queryPipeline Query pipeline
     * @param selectClause Select clause if any This is only for validation.
     * @return Query action node
     */
    private STNode parseQueryAction(STNode queryPipeline, STNode selectClause) {
        if (selectClause != null) {
            this.errorHandler.reportInvalidNode(null, "cannot have a select clause in query action");
        }

        startContext(ParserRuleContext.DO_CLAUSE);
        STNode doKeyword = parseDoKeyword();
        STNode blockStmt = parseBlockNode();
        endContext();
        return STNodeFactory.createQueryActionNode(queryPipeline, doKeyword, blockStmt);
    }

    /**
     * Parse 'do' keyword.
     *
     * @return do keyword node
     */
    private STNode parseDoKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.DO_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.DO_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse optional field access or xml optional attribute access expression.
     * <p>
     * <code>
     * optional-field-access-expr := expression ?. field-name
     * <br/>
     * xml-optional-attribute-access-expr := expression ?. xml-attribute-name
     * <br/>
     * xml-attribute-name := xml-qualified-name | qualified-identifier | identifier
     * <br/>
     * xml-qualified-name := xml-namespace-prefix : identifier
     * <br/>
     * xml-namespace-prefix := identifier
     * </code>
     *
     * @param lhsExpr Preceding expression of the optional access
     * @return Parsed node
     */
    private STNode parseOptionalFieldAccessExpression(STNode lhsExpr) {
        STNode optionalFieldAccessToken = parseOptionalChainingToken();
        STNode fieldName = parseFieldAccessIdentifier();
        return STNodeFactory.createOptionalFieldAccessExpressionNode(lhsExpr, optionalFieldAccessToken, fieldName);
    }

    /**
     * Parse optional chaining token.
     *
     * @return parsed node
     */
    private STNode parseOptionalChainingToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.OPTIONAL_CHAINING_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.OPTIONAL_CHAINING_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse conditional expression.
     * <p>
     * <code>conditional-expr := expression ? expression : expression</code>
     *
     * @param lhsExpr Preceding expression of the question mark
     * @return Parsed node
     */
    private STNode parseConditionalExpression(STNode lhsExpr) {
        startContext(ParserRuleContext.CONDITIONAL_EXPRESSION);
        STNode questionMark = parseQuestionMark();
        STNode middleExpr = parseExpression(OperatorPrecedence.ELVIS_CONDITIONAL, true, false);
        STNode colon = parseColon();
        endContext();
        STNode endExpr = parseExpression(OperatorPrecedence.ELVIS_CONDITIONAL, true, false);
        return STNodeFactory.createConditionalExpressionNode(lhsExpr, questionMark, middleExpr, colon, endExpr);
    }

    /**
     * Parse enum declaration.
     * <p>
     * module-enum-decl :=
     * metadata
     * [public] enum identifier { enum-member (, enum-member)* }
     * enum-member := metadata identifier [= const-expr]
     * </p>
     *
     * @param metadata
     * @param qualifier
     *
     * @return Parsed enum node.
     */
    private STNode parseEnumDeclaration(STNode metadata, STNode qualifier) {
        startContext(ParserRuleContext.MODULE_ENUM_DECLARATION);
        STNode enumKeywordToken = parseEnumKeyword();
        STNode identifier = parseIdentifier(ParserRuleContext.MODULE_ENUM_NAME);
        STNode openBraceToken = parseOpenBrace();
        STNode enumMemberList = parseEnumMemberList();
        STNode closeBraceToken = parseCloseBrace();

        endContext();
        return STNodeFactory.createEnumDeclarationNode(metadata, qualifier, enumKeywordToken, identifier,
                openBraceToken, enumMemberList, closeBraceToken);
    }

    /**
     * Parse 'enum' keyword.
     *
     * @return enum keyword node
     */
    private STNode parseEnumKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ENUM_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ENUM_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse enum member list.
     * <p>
     * enum-member := metadata identifier [= const-expr]
     * </p>
     *
     * @return enum member list node.
     */
    private STNode parseEnumMemberList() {
        startContext(ParserRuleContext.ENUM_MEMBER_LIST);
        List<STNode> enumMemberList = new ArrayList<>();
        STToken nextToken = peek();

        
        if (nextToken.kind == SyntaxKind.CLOSE_BRACE_TOKEN) {
            this.errorHandler.reportMissingTokenError("enum member list cannot be empty");
            return STNodeFactory.createNodeList(new ArrayList<>());
        }

        
        STNode enumMember = parseEnumMember();

        
        nextToken = peek();
        STNode enumMemberRhs;
        while (nextToken.kind != SyntaxKind.CLOSE_BRACE_TOKEN) {
            enumMemberRhs = parseEnumMemberEnd(nextToken.kind);
            if (enumMemberRhs == null) {
                break;
            }
            enumMemberList.add(enumMember);
            enumMemberList.add(enumMemberRhs);
            enumMember = parseEnumMember();
            nextToken = peek();
        }

        enumMemberList.add(enumMember);

        endContext();
        return STNodeFactory.createNodeList(enumMemberList);
    }

    /**
     * Parse enum member.
     * <p>
     * enum-member := metadata identifier [= const-expr]
     * </p>
     *
     * @return Parsed enum member node.
     */
    private STNode parseEnumMember() {
        STToken nextToken = peek();
        STNode metadata;
        switch (nextToken.kind) {
            case DOCUMENTATION_LINE:
            case AT_TOKEN:
                metadata = parseMetaData(nextToken.kind);
                break;
            default:
                metadata = STNodeFactory.createEmptyNode();
        }

        STNode identifierNode = parseIdentifier(ParserRuleContext.ENUM_MEMBER_NAME);
        return parseEnumMemberRhs(metadata, identifierNode);
    }

    private STNode parseEnumMemberRhs(STNode metadata, STNode identifierNode) {
        return parseEnumMemberRhs(peek().kind, metadata, identifierNode);
    }

    private STNode parseEnumMemberRhs(SyntaxKind nextToken, STNode metadata, STNode identifierNode) {
        STNode equalToken, constExprNode;
        switch (nextToken) {
            case EQUAL_TOKEN:
                equalToken = parseAssignOp();
                constExprNode = parseExpression();
                break;
            case COMMA_TOKEN:
            case CLOSE_BRACE_TOKEN:
                equalToken = STNodeFactory.createEmptyNode();
                constExprNode = STNodeFactory.createEmptyNode();
                break;
            default:
                Solution solution = recover(peek(), ParserRuleContext.ENUM_MEMBER_RHS, metadata, identifierNode);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseEnumMemberRhs(solution.tokenKind, metadata, identifierNode);
        }

        return STNodeFactory.createEnumMemberNode(metadata, identifierNode, equalToken, constExprNode);
    }

    private STNode parseEnumMemberEnd() {
        return parseEnumMemberEnd(peek().kind);
    }

    private STNode parseEnumMemberEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACE_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.ENUM_MEMBER_END);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseEnumMemberEnd(solution.tokenKind);
        }
    }

    /**
     * Parse transaction statement.
     * <p>
     * <code>transaction-stmt := "transaction" block-stmt ;</code>
     *
     * @return Transaction statement node
     */
    private STNode parseTransactionStatement() {
        startContext(ParserRuleContext.TRANSACTION_STMT);
        STNode transactionKeyword = parseTransactionKeyword();
        STNode blockStmt = parseBlockNode();
        endContext();
        return STNodeFactory.createTransactionStatementNode(transactionKeyword, blockStmt);
    }

    /**
     * Parse transaction keyword.
     *
     * @return parsed node
     */
    private STNode parseTransactionKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.TRANSACTION_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.TRANSACTION_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse commit action.
     * <p>
     * <code>commit-action := "commit"</code>
     *
     * @return Commit action node
     */
    private STNode parseCommitAction() {
        STNode commitKeyword = parseCommitKeyword();
        return STNodeFactory.createCommitActionNode(commitKeyword);
    }

    /**
     * Parse commit keyword.
     *
     * @return parsed node
     */
    private STNode parseCommitKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.COMMIT_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.COMMIT_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse retry statement.
     * <p>
     * <code>
     * retry-stmt := "retry" retry-spec block-stmt
     * <br/>
     * retry-spec :=  [type-parameter] [ "(" arg-list ")" ]
     * </code>
     *
     * @return Retry statement node
     */
    private STNode parseRetryStatement() {
        startContext(ParserRuleContext.RETRY_STMT);
        STNode retryKeyword = parseRetryKeyword();
        STNode retryStmt = parseRetryKeywordRhs(retryKeyword);
        endContext();
        return retryStmt;
    }

    private STNode parseRetryKeywordRhs(STNode retryKeyword) {
        return parseRetryKeywordRhs(peek().kind, retryKeyword);
    }

    private STNode parseRetryKeywordRhs(SyntaxKind nextTokenKind, STNode retryKeyword) {
        switch (nextTokenKind) {
            case LT_TOKEN:
                STNode typeParam = parseTypeParameter();
                return parseRetryTypeParamRhs(retryKeyword, typeParam);
            case OPEN_PAREN_TOKEN:
            case OPEN_BRACE_TOKEN:
            case TRANSACTION_KEYWORD:
                typeParam = STNodeFactory.createEmptyNode();
                return parseRetryTypeParamRhs(nextTokenKind, retryKeyword, typeParam);
            default:
                Solution solution = recover(peek(), ParserRuleContext.RETRY_KEYWORD_RHS, retryKeyword);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseRetryKeywordRhs(solution.tokenKind, retryKeyword);
        }
    }

    private STNode parseRetryTypeParamRhs(STNode retryKeyword, STNode typeParam) {
        return parseRetryTypeParamRhs(peek().kind, retryKeyword, typeParam);
    }

    private STNode parseRetryTypeParamRhs(SyntaxKind nextTokenKind, STNode retryKeyword, STNode typeParam) {
        STNode args;
        switch (nextTokenKind) {
            case OPEN_PAREN_TOKEN:
                args = parseParenthesizedArgList();
                break;
            case OPEN_BRACE_TOKEN:
            case TRANSACTION_KEYWORD:
                args = STNodeFactory.createEmptyNode();
                break;
            default:
                Solution solution = recover(peek(), ParserRuleContext.RETRY_TYPE_PARAM_RHS, retryKeyword, typeParam);
                return parseRetryTypeParamRhs(solution.tokenKind, retryKeyword, typeParam);
        }

        STNode blockStmt = parseRetryBody();
        return STNodeFactory.createRetryStatementNode(retryKeyword, typeParam, args, blockStmt);
    }

    private STNode parseRetryBody() {
        return parseRetryBody(peek().kind);
    }

    private STNode parseRetryBody(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case OPEN_BRACE_TOKEN:
                return parseBlockNode();
            case TRANSACTION_KEYWORD:
                return parseTransactionStatement();
            default:
                Solution solution = recover(peek(), ParserRuleContext.RETRY_BODY);
                return parseRetryBody(solution.tokenKind);
        }
    }

    /**
     * Parse retry keyword.
     *
     * @return parsed node
     */
    private STNode parseRetryKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.RETRY_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.RETRY_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse transaction statement.
     * <p>
     * <code>rollback-stmt := "rollback" [expression] ";"</code>
     *
     * @return Rollback statement node
     */
    private STNode parseRollbackStatement() {
        startContext(ParserRuleContext.ROLLBACK_STMT);
        STNode rollbackKeyword = parseRollbackKeyword();
        STNode expression;
        if (peek().kind == SyntaxKind.SEMICOLON_TOKEN) {
            expression = STNodeFactory.createEmptyNode();
        } else {
            expression = parseExpression();
        }

        STNode semicolon = parseSemicolon();
        endContext();
        return STNodeFactory.createRollbackStatementNode(rollbackKeyword, expression, semicolon);
    }

    /**
     * Parse rollback keyword.
     *
     * @return Rollback keyword node
     */
    private STNode parseRollbackKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.ROLLBACK_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ROLLBACK_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse transactional expression.
     * <p>
     * <code>transactional-expr := "transactional"</code>
     *
     * @return Transactional expression node
     */
    private STNode parseTransactionalExpression() {
        STNode transactionalKeyword = parseTransactionalKeyword();
        return STNodeFactory.createTransactionalExpressionNode(transactionalKeyword);
    }

    /**
     * Parse transactional keyword.
     *
     * @return Transactional keyword node
     */
    private STNode parseTransactionalKeyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.TRANSACTIONAL_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.ROLLBACK_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse service-constructor-expr.
     * <p>
     * <code>
     * service-constructor-expr := [annots] service service-body-block
     * <br/>
     * service-body-block := { service-method-defn* }
     * <br/>
     * service-method-defn := metadata [resource] function identifier function-signature method-defn-body
     * </code>
     *
     * @param annots Annotations
     * @return Service constructor expression node
     */
    private STNode parseServiceConstructorExpression(STNode annots) {
        startContext(ParserRuleContext.SERVICE_CONSTRUCTOR_EXPRESSION);
        STNode serviceKeyword = parseServiceKeyword();
        STNode serviceBody = parseServiceBody();
        endContext();
        return STNodeFactory.createServiceConstructorExpressionNode(annots, serviceKeyword, serviceBody);
    }

    /**
     * Parse base16 literal.
     * <p>
     * <code>
     * byte-array-literal := Base16Literal | Base64Literal
     * <br/>
     * Base16Literal := base16 WS ` HexGroup* WS `
     * <br/>
     * Base64Literal := base64 WS ` Base64Group* [PaddedBase64Group] WS `
     * </code>
     *
     * @param kind byte array literal kind
     * @return parsed node
     */
    private STNode parseByteArrayLiteral(SyntaxKind kind) {
        STNode type;

        if (kind == SyntaxKind.BASE16_KEYWORD) {
            type = parseBase16Keyword();
        } else {
            type = parseBase64Keyword();
        }

        STNode startingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_START);
        STNode content = parseByteArrayContent(kind);
        STNode endingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_END);
        return STNodeFactory.createByteArrayLiteralNode(type, startingBackTick, content, endingBackTick);
    }

    /**
     * Parse <code>base16</code> keyword.
     *
     * @return base16 keyword node
     */
    private STNode parseBase16Keyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.BASE16_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.BASE16_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse <code>base64</code> keyword.
     *
     * @return base64 keyword node
     */
    private STNode parseBase64Keyword() {
        STToken token = peek();
        if (token.kind == SyntaxKind.BASE64_KEYWORD) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.BASE64_KEYWORD);
            return sol.recoveredNode;
        }
    }

    /**
     * Validate and parse byte array literal content.
     * An error is reported, if the content is invalid.
     *
     * @param kind byte array literal kind
     * @return parsed node
     */
    private STNode parseByteArrayContent(SyntaxKind kind) {
        STNode content = STNodeFactory.createEmptyNode();
        STToken nextToken = peek();

        List<STNode> items = new ArrayList<>();
        while (!isEndOfBacktickContent(nextToken.kind)) {
            content = parseTemplateItem();
            items.add(content);
            nextToken = peek();
        }

        if (items.size() > 1) {
            this.errorHandler.reportInvalidNode(null, "invalid content within backticks");
        } else if (items.size() == 1 && content.kind != SyntaxKind.TEMPLATE_STRING) {
            this.errorHandler.reportInvalidNode(null, "invalid content within backticks");
        } else if (items.size() == 1) {
            if (kind == SyntaxKind.BASE16_KEYWORD && !BallerinaLexer.isValidBase16LiteralContent(content.toString())) {
                this.errorHandler.reportInvalidNode(null, "invalid content within backticks");
            } else if (kind == SyntaxKind.BASE64_KEYWORD &&
                    !BallerinaLexer.isValidBase64LiteralContent(content.toString())) {
                this.errorHandler.reportInvalidNode(null, "invalid content within backticks");
            }
        }

        return content;
    }

    /**
     * Parse xml filter expression.
     * <p>
     * <code>xml-filter-expr := expression .< xml-name-pattern ></code>
     *
     * @param lhsExpr Preceding expression of .< token
     * @return Parsed node
     */
    private STNode parseXMLFilterExpression(STNode lhsExpr) {
        STNode xmlNamePatternChain = parseXMLFilterExpressionRhs();
        return STNodeFactory.createXMLFilterExpressionNode(lhsExpr, xmlNamePatternChain);
    }

    /**
     * Parse xml filter expression rhs.
     * <p>
     * <code>filer-expression-rhs := .< xml-name-pattern ></code>
     *
     * @return Parsed node
     */
    private STNode parseXMLFilterExpressionRhs() {
        STNode dotLTToken = parseDotLTToken();
        return parseXMLNamePatternChain(dotLTToken);
    }

    /**
     * Parse xml name pattern chain.
     * <p>
     * <code>
     * xml-name-pattern-chain := filer-expression-rhs | xml-element-children-step | xml-element-descendants-step
     * <br/>
     * filer-expression-rhs := .< xml-name-pattern >
     * <br/>
     * xml-element-children-step := /< xml-name-pattern >
     * <br/>
     * xml-element-descendants-step := /**\/<xml-name-pattern >
     * </code>
     *
     * @param startToken Preceding token of xml name pattern
     * @return Parsed node
     */
    private STNode parseXMLNamePatternChain(STNode startToken) {
        startContext(ParserRuleContext.XML_NAME_PATTERN);
        STNode xmlNamePattern = parseXMLNamePattern();
        STNode gtToken = parseGTToken();
        endContext();
        return STNodeFactory.createXMLNamePatternChainingNode(startToken, xmlNamePattern, gtToken);
    }

    /**
     * Parse <code> .< </code> token.
     *
     * @return Parsed node
     */
    private STNode parseDotLTToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.DOT_LT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.DOT_LT_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse xml name pattern.
     * <p>
     * <code>xml-name-pattern := xml-atomic-name-pattern [| xml-atomic-name-pattern]*</code>
     *
     * @return Parsed node
     */
    private STNode parseXMLNamePattern() {
        List<STNode> xmlAtomicNamePatternList = new ArrayList<>();
        STToken nextToken = peek();

        
        if (isEndOfXMLNamePattern(nextToken.kind)) {
            this.errorHandler.reportMissingTokenError("missing xml atomic name pattern");
            return STNodeFactory.createNodeList(xmlAtomicNamePatternList);
        }

        
        STNode xmlAtomicNamePattern = parseXMLAtomicNamePattern();
        xmlAtomicNamePatternList.add(xmlAtomicNamePattern);

        
        STNode leadingPipe;
        while (!isEndOfXMLNamePattern(peek().kind)) {
            leadingPipe = parsePipeToken();
            xmlAtomicNamePatternList.add(leadingPipe);

            xmlAtomicNamePattern = parseXMLAtomicNamePattern();
            xmlAtomicNamePatternList.add(xmlAtomicNamePattern);
        }

        return STNodeFactory.createNodeList(xmlAtomicNamePatternList);
    }

    private boolean isEndOfXMLNamePattern(SyntaxKind tokenKind) {
        switch (tokenKind) {
            case IDENTIFIER_TOKEN:
            case ASTERISK_TOKEN:
            case COLON_TOKEN:
                return false;
            case GT_TOKEN:
            case EOF_TOKEN:
            case AT_TOKEN:
            case DOCUMENTATION_LINE:
            case CLOSE_BRACE_TOKEN:
            case SEMICOLON_TOKEN:
            case PUBLIC_KEYWORD:
            case PRIVATE_KEYWORD:
            case FUNCTION_KEYWORD:
            case RETURNS_KEYWORD:
            case SERVICE_KEYWORD:
            case TYPE_KEYWORD:
            case LISTENER_KEYWORD:
            case CONST_KEYWORD:
            case FINAL_KEYWORD:
            case RESOURCE_KEYWORD:
                return true;
            default:
                return isSimpleType(tokenKind);
        }
    }

    /**
     * Parse xml atomic name pattern.
     * <p>
     * <code>
     * xml-atomic-name-pattern :=
     *   *
     *   | identifier
     *   | xml-namespace-prefix : identifier
     *   | xml-namespace-prefix : *
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseXMLAtomicNamePattern() {
        startContext(ParserRuleContext.XML_ATOMIC_NAME_PATTERN);
        STNode atomicNamePattern = parseXMLAtomicNamePatternBody();
        endContext();
        return atomicNamePattern;
    }

    private STNode parseXMLAtomicNamePatternBody() {
        STToken token = peek();
        STNode identifier;
        switch (token.kind) {
            case ASTERISK_TOKEN:
                return consume();
            case IDENTIFIER_TOKEN:
                identifier = consume();
                break;
            default:
                Solution sol = recover(token, ParserRuleContext.XML_ATOMIC_NAME_PATTERN_START);
                if (sol.action == Action.REMOVE) {
                    return sol.recoveredNode;
                }

                if (sol.recoveredNode.kind == SyntaxKind.ASTERISK_TOKEN) {
                    return sol.recoveredNode;
                }

                identifier = sol.recoveredNode;
                break;
        }

        return parseXMLAtomicNameIdentifier(identifier);
    }

    private STNode parseXMLAtomicNameIdentifier(STNode identifier) {
        STToken token = peek();
        if (token.kind == SyntaxKind.COLON_TOKEN) {
            STNode colon = consume();
            STToken nextToken = peek();
            if (nextToken.kind == SyntaxKind.IDENTIFIER_TOKEN || nextToken.kind == SyntaxKind.ASTERISK_TOKEN) {
                STToken endToken = consume();
                return STNodeFactory.createXMLAtomicNamePatternNode(identifier, colon, endToken);
            }
        }
        return STNodeFactory.createSimpleNameReferenceNode(identifier);
    }

    /**
     * Parse xml step expression.
     * <p>
     * <code>xml-step-expr := expression xml-step-start</code>
     *
     * @param lhsExpr Preceding expression of /*, /<, or /**\/< token
     * @return Parsed node
     */
    private STNode parseXMLStepExpression(STNode lhsExpr) {
        STNode xmlStepStart = parseXMLStepStart();
        return STNodeFactory.createXMLStepExpressionNode(lhsExpr, xmlStepStart);
    }

    /**
     * Parse xml filter expression rhs.
     * <p>
     * <code>
     *  xml-step-start :=
     *      xml-all-children-step
     *      | xml-element-children-step
     *      | xml-element-descendants-step
     * <br/>
     * xml-all-children-step := /*
     * </code>
     *
     * @return Parsed node
     */
    private STNode parseXMLStepStart() {
        STToken token = peek();
        STNode startToken;

        switch (token.kind) {
            case SLASH_ASTERISK_TOKEN:
                return consume();
            case DOUBLE_SLASH_DOUBLE_ASTERISK_LT_TOKEN:
                startToken = parseDoubleSlashDoubleAsteriskLTToken();
                break;
            case SLASH_LT_TOKEN:
            default:
                startToken = parseSlashLTToken();
                break;
        }
        return parseXMLNamePatternChain(startToken);
    }

    /**
     * Parse <code> /< </code> token.
     *
     * @return Parsed node
     */
    private STNode parseSlashLTToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.SLASH_LT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.SLASH_LT_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Parse <code> /< </code> token.
     *
     * @return Parsed node
     */
    private STNode parseDoubleSlashDoubleAsteriskLTToken() {
        STToken nextToken = peek();
        if (nextToken.kind == SyntaxKind.DOUBLE_SLASH_DOUBLE_ASTERISK_LT_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(nextToken, ParserRuleContext.DOUBLE_SLASH_DOUBLE_ASTERISK_LT_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Returns 'true' if the list is empty.
     * <p>
     * First check whether this node is an instance of STNodeList.
     *
     * @param node the nodelist instance
     * @return returns 'true' if the list is empty
     */
    private boolean isNodeListEmpty(STNode node) {
        if (!NodeListUtils.isSTNodeList(node)) {
            throw new IllegalArgumentException("The 'node' should be an instance of STNodeList");
        }

        STNodeList nodeList = (STNodeList) node;
        return nodeList.isEmpty();
    }

    /**
     * Returns a clone of the given STNode with the given diagnostic if the nodeList is empty,
     * otherwise returns the original STNode.
     *
     * @param nodeList the node list instance
     * @param target the STNode instance
     * @param diagnosticCode the DiagnosticCode to be added to the node
     * @return a clone of the given STNode
     */
    private STNode addDiagnosticIfListEmpty(STNode nodeList, STNode target, DiagnosticCode diagnosticCode) {
        if (isNodeListEmpty(nodeList)) {
            return errorHandler.addDiagnostics(target, diagnosticCode);
        }
        return target;
    }

    

    /**
     * Parse binding-patterns.
     * <p>
     * <code>
     * binding-pattern := capture-binding-pattern
     *                      | wildcard-binding-pattern
     *                      | list-binding-pattern
     *                      | mapping-binding-pattern
     *                      | functional-binding-pattern
     * <br/><br/>
     *
     * capture-binding-pattern := variable-name
     * variable-name := identifier
     * <br/><br/>
     *
     * wildcard-binding-pattern := _
     * list-binding-pattern := [ list-member-binding-patterns ]
     * <br/>
     * list-member-binding-patterns := binding-pattern (, binding-pattern)* [, rest-binding-pattern]
     *                                 | [ rest-binding-pattern ]
     * <br/><br/>
     *
     * mapping-binding-pattern := { field-binding-patterns }
     * field-binding-patterns := field-binding-pattern (, field-binding-pattern)* [, rest-binding-pattern]
     *                          | [ rest-binding-pattern ]
     * <br/>
     * field-binding-pattern := field-name : binding-pattern | variable-name
     * <br/>
     * rest-binding-pattern := ... variable-name
     *
     * <br/><br/>
     * functional-binding-pattern := functionally-constructible-type-reference ( arg-list-binding-pattern )
     * <br/>
     * arg-list-binding-pattern := positional-arg-binding-patterns [, other-arg-binding-patterns]
     *                             | other-arg-binding-patterns
     * <br/>
     * positional-arg-binding-patterns := positional-arg-binding-pattern (, positional-arg-binding-pattern)*
     * <br/>
     * positional-arg-binding-pattern := binding-pattern
     * <br/>
     * other-arg-binding-patterns := named-arg-binding-patterns [, rest-binding-pattern]
     *                              | [rest-binding-pattern]
     * <br/>
     * named-arg-binding-patterns := named-arg-binding-pattern (, named-arg-binding-pattern)*
     * <br/>
     * named-arg-binding-pattern := arg-name = binding-pattern
     *</code>
     *
     * @return binding-pattern node
     */
    private STNode parseBindingPattern() {
        STToken token = peek();
        return parseBindingPattern(token.kind);
    }

    private STNode parseBindingPattern(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case OPEN_BRACKET_TOKEN:
                return parseListBindingPattern();
            case IDENTIFIER_TOKEN:
                return parseCaptureOrWildcardBindingPattern();
            case OPEN_BRACE_TOKEN:
                return parseMappingBindingPattern();
            default:
                Solution sol = recover(peek(), ParserRuleContext.BINDING_PATTERN);

                
                
                
                if (sol.action == Action.REMOVE) {
                    return sol.recoveredNode;
                }

                return parseBindingPattern(sol.tokenKind);
        }
    }

    /**
     * Parse capture-binding-pattern.
     * <p>
     * <code>
     * capture-binding-pattern := variable-name
     * <br/>
     * variable-name := identifier
     * </code>
     *
     * @return capture-binding-pattern node
     */
    private STNode parseCaptureOrWildcardBindingPattern() {
        STToken token = peek();
        if (token.kind == SyntaxKind.IDENTIFIER_TOKEN) {
            STNode varName = parseVariableName();
            return createCaptureOrWildcardBP(varName);
        } else {
            Solution sol = recover(token, ParserRuleContext.CAPTURE_BINDING_PATTERN);
            if (sol.action == Action.REMOVE) {
                return sol.recoveredNode;
            }

            return STNodeFactory.createCaptureBindingPatternNode(sol.recoveredNode);
        }
    }

    private STNode createCaptureOrWildcardBP(STNode varName) {
        STNode bindingPattern;
        if (isWildcardBP(varName)) {
            bindingPattern = getWildcardBindingPattern(varName);
        } else {
            bindingPattern = STNodeFactory.createCaptureBindingPatternNode(varName);
        }
        return bindingPattern;
    }

    /**
     * Parse list-binding-patterns.
     * <p>
     * <code>
     * list-binding-pattern := [ list-member-binding-patterns ]
     * <br/>
     * list-member-binding-patterns := binding-pattern (, binding-pattern)* [, rest-binding-pattern]
     *                                | [ rest-binding-pattern ]
     * </code>
     *
     * @return list-binding-pattern node
     */
    private STNode parseListBindingPattern() {
        startContext(ParserRuleContext.LIST_BINDING_PATTERN);
        STNode openBracket = parseOpenBracket();
        List<STNode> bindingPatternsList = new ArrayList<>();
        STNode listBindingPattern = parseListBindingPattern(openBracket, bindingPatternsList);
        endContext();
        return listBindingPattern;
    }

    private STNode parseListBindingPattern(STNode openBracket, List<STNode> bindingPatternsList) {
        STNode listBindingPatternMember = parseListBindingPatternMember();
        bindingPatternsList.add(listBindingPatternMember);
        STNode listBindingPattern = parseListBindingPattern(openBracket, listBindingPatternMember, bindingPatternsList);
        return listBindingPattern;
    }

    private STNode parseListBindingPattern(STNode openBracket, STNode firstMember, List<STNode> bindingPatterns) {
        STNode member = firstMember;
        
        STToken token = peek(); 
        STNode listBindingPatternRhs = null;
        while (!isEndOfListBindingPattern(token.kind) && member.kind != SyntaxKind.REST_BINDING_PATTERN) {
            listBindingPatternRhs = parseListBindingpatternRhs(token.kind);
            if (listBindingPatternRhs == null) {
                break;
            }

            bindingPatterns.add(listBindingPatternRhs);
            member = parseListBindingPatternMember();
            bindingPatterns.add(member);
            token = peek();
        }

        
        STNode restBindingPattern;
        if (member.kind == SyntaxKind.REST_BINDING_PATTERN) {
            restBindingPattern = bindingPatterns.remove(bindingPatterns.size() - 1);
        } else {
            restBindingPattern = STNodeFactory.createEmptyNode();
        }

        STNode closeBracket = parseCloseBracket();
        STNode bindingPatternsNode = STNodeFactory.createNodeList(bindingPatterns);
        return STNodeFactory.createListBindingPatternNode(openBracket, bindingPatternsNode, restBindingPattern,
                closeBracket);
    }

    private STNode parseListBindingpatternRhs() {
        return parseListBindingpatternRhs(peek().kind);
    }

    private STNode parseListBindingpatternRhs(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACKET_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.LIST_BINDING_PATTERN_END_OR_CONTINUE);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseListBindingpatternRhs(solution.tokenKind);
        }
    }

    private boolean isEndOfListBindingPattern(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case CLOSE_BRACKET_TOKEN:
            case EOF_TOKEN:
                return true;
            default:
                return false;
        }
    }

    /**
     * Parse list-binding-pattern entry.
     * <p>
     * <code>
     * list-binding-pattern := [ list-member-binding-patterns ]
     * <br/>
     * list-member-binding-patterns := binding-pattern (, binding-pattern)* [, rest-binding-pattern]
     *                                  | [ rest-binding-pattern ]
     * </code>
     *
     * @return rest-binding-pattern node
     */
    private STNode parseListBindingPatternMember() {
        STToken token = peek();
        switch (token.kind) {
            case ELLIPSIS_TOKEN:
                return parseRestBindingPattern();
            default:
                return parseBindingPattern();
        }
    }

    private STNode parseRestBindingPattern() {
        startContext(ParserRuleContext.REST_BINDING_PATTERN);
        STNode ellipsis = parseEllipsis();
        STNode varName = parseVariableName();
        endContext();
        return STNodeFactory.createRestBindingPatternNode(ellipsis, varName);
    }

    /**
     * Parse Typed-binding-pattern.
     * <p>
     * <code>
     * typed-binding-pattern := inferable-type-descriptor binding-pattern
     * <br/><br/>
     * inferable-type-descriptor := type-descriptor | var
     * </code>
     *
     * @return Typed binding pattern node
     */
    private STNode parseTypedBindingPattern(ParserRuleContext context) {
        STNode typeDesc = parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN, true);
        STNode typeBindingPattern = parseTypedBindingPatternTypeRhs(typeDesc, context);
        return typeBindingPattern;
    }

    /**
     * Parse mapping-binding-patterns.
     * <p>
     * <code>
     * mapping-binding-pattern := { field-binding-patterns }
     * <br/><br/>
     * field-binding-patterns := field-binding-pattern (, field-binding-pattern)* [, rest-binding-pattern]
     *                          | [ rest-binding-pattern ]
     * <br/><br/>
     * field-binding-pattern := field-name : binding-pattern | variable-name
     * </code>
     *
     * @return mapping-binding-pattern node
     */
    private STNode parseMappingBindingPattern() {
        startContext(ParserRuleContext.MAPPING_BINDING_PATTERN);
        List<STNode> bindingPatterns = new ArrayList<>();
        STNode openBrace = parseOpenBrace();

        STToken token = peek();
        if (isEndOfMappingBindingPattern(token.kind)) {
            STNode closeBrace = parseCloseBrace();
            STNode bindingPatternsNode = STNodeFactory.createNodeList(bindingPatterns);
            STNode restBindingPattern = STNodeFactory.createEmptyNode();
            endContext();
            return STNodeFactory.createMappingBindingPatternNode(openBrace, bindingPatternsNode, restBindingPattern,
                    closeBrace);
        }

        STNode prevMember = parseMappingBindingPatternMember();
        bindingPatterns.add(prevMember);
        return parseMappingBindingPattern(openBrace, bindingPatterns, prevMember);
    }

    private STNode parseMappingBindingPattern(STNode openBrace, List<STNode> bindingPatterns, STNode member) {
        STToken token = peek(); 
        STNode mappingBindingPatternRhs = null;
        while (!isEndOfMappingBindingPattern(token.kind) && member.kind != SyntaxKind.REST_BINDING_PATTERN) {
            mappingBindingPatternRhs = parseMappingBindingpatternEnd(token.kind);
            if (mappingBindingPatternRhs == null) {
                break;
            }

            bindingPatterns.add(mappingBindingPatternRhs);
            member = parseMappingBindingPatternMember();
            if (member.kind == SyntaxKind.REST_BINDING_PATTERN) {
                break;
            }
            bindingPatterns.add(member);
            token = peek();
        }

        
        STNode restBindingPattern;
        if (member.kind == SyntaxKind.REST_BINDING_PATTERN) {
            restBindingPattern = member;
        } else {
            restBindingPattern = STNodeFactory.createEmptyNode();
        }

        STNode closeBrace = parseCloseBrace();
        STNode bindingPatternsNode = STNodeFactory.createNodeList(bindingPatterns);
        endContext();
        return STNodeFactory.createMappingBindingPatternNode(openBrace, bindingPatternsNode, restBindingPattern,
                closeBrace);
    }

    /**
     * Parse mapping-binding-pattern entry.
     * <p>
     * <code>
     * mapping-binding-pattern := { field-binding-patterns }
     * <br/><br/>
     * field-binding-patterns := field-binding-pattern (, field-binding-pattern)* [, rest-binding-pattern]
     *                          | [ rest-binding-pattern ]
     * <br/><br/>
     * field-binding-pattern := field-name : binding-pattern
     *                          | variable-name
     * </code>
     *
     * @return mapping-binding-pattern node
     */
    private STNode parseMappingBindingPatternMember() {
        STToken token = peek();
        switch (token.kind) {
            case ELLIPSIS_TOKEN:
                return parseRestBindingPattern();
            default:
                return parseFieldBindingPattern();
        }
    }

    private STNode parseMappingBindingpatternEnd() {
        return parseMappingBindingpatternEnd(peek().kind);
    }

    private STNode parseMappingBindingpatternEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACE_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.MAPPING_BINDING_PATTERN_END);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseMappingBindingpatternEnd(solution.tokenKind);
        }
    }

    private STNode parseFieldBindingPattern() {
        return parseFieldBindingPattern(peek().kind);
    }

    /**
     * Parse field-binding-pattern.
     * <code>field-binding-pattern := field-name : binding-pattern | varname</code>
     *
     * @return field-binding-pattern node
     */
    private STNode parseFieldBindingPattern(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN:
                STNode identifier = parseIdentifier(ParserRuleContext.FIELD_BINDING_PATTERN_NAME);
                STNode fieldBindingPattern = parseFieldBindingPattern(identifier);
                return fieldBindingPattern;
            default:
                Solution solution = recover(peek(), ParserRuleContext.FIELD_BINDING_PATTERN_NAME);

                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseFieldBindingPattern(solution.tokenKind);
        }
    }

    private STNode parseFieldBindingPattern(STNode identifier) {
        if (peek().kind != SyntaxKind.COLON_TOKEN) {
            return STNodeFactory.createFieldBindingPatternVarnameNode(identifier);
        }

        STNode colon = parseColon();
        STNode bindingPattern = parseBindingPattern();

        return STNodeFactory.createFieldBindingPatternFullNode(identifier, colon, bindingPattern);
    }

    private boolean isEndOfMappingBindingPattern(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case CLOSE_BRACE_TOKEN:
                return true;
            default:
                return false;
        }
    }

    

    /*
     * This parses Typed binding patterns and deals with ambiguity between types,
     * and binding patterns. An example is 'T[a]'.
     * The ambiguity lies in between:
     * 1) Array Type
     * 2) List binding pattern
     * 3) Member access expression.
     */

    /**
     * Parse the component after the type-desc, of a typed-binding-pattern.
     *
     * @param typeDesc Starting type-desc of the typed-binding-pattern
     * @return Typed-binding pattern
     */
    private STNode parseTypedBindingPatternTypeRhs(STNode typeDesc, ParserRuleContext context) {
        return parseTypedBindingPatternTypeRhs(peek().kind, typeDesc, context);
    }

    private STNode parseTypedBindingPatternTypeRhs(SyntaxKind nextTokenKind, STNode typeDesc,
                                                   ParserRuleContext context) {
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN: 
            case OPEN_BRACE_TOKEN: 
            case ERROR_KEYWORD: 
                STNode bindingPattern = parseBindingPattern(nextTokenKind);
                return STNodeFactory.createTypedBindingPatternNode(typeDesc, bindingPattern);
            case OPEN_BRACKET_TOKEN:
                
                STNode typedBindingPattern = parseTypedBindingPatternOrMemberAccess(typeDesc, true, context);
                assert typedBindingPattern.kind == SyntaxKind.TYPED_BINDING_PATTERN;
                return typedBindingPattern;
            default:
                Solution solution =
                        recover(peek(), ParserRuleContext.TYPED_BINDING_PATTERN_TYPE_RHS, typeDesc, context);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseTypedBindingPatternTypeRhs(solution.tokenKind, typeDesc, context);
        }
    }

    /**
     * Parse typed-binding pattern with list, array-type-desc, or member-access-expr.
     *
     * @param typeDescOrExpr Type desc or the expression at the start
     * @param isTypedBindingPattern Is this is a typed-binding-pattern.
     * @return Parsed node
     */
    private STNode parseTypedBindingPatternOrMemberAccess(STNode typeDescOrExpr, boolean isTypedBindingPattern,
                                                          ParserRuleContext context) {
        startContext(ParserRuleContext.BRACKETED_LIST);
        STNode openBracket = parseOpenBracket();

        
        if (isBracketedListEnd(peek().kind)) {
            return parseAsArrayTypeDesc(typeDescOrExpr, openBracket, STNodeFactory.createEmptyNode(), context);
        }

        
        STNode member = parseBracketedListMember(isTypedBindingPattern);
        SyntaxKind currentNodeType = getBracketedListNodeType(member);
        switch (currentNodeType) {
            case ARRAY_TYPE_DESC:
                STNode typedBindingPattern = parseAsArrayTypeDesc(typeDescOrExpr, openBracket, member, context);
                return typedBindingPattern;
            case LIST_BINDING_PATTERN:
                
                
                STNode bindingPattern = parseAsListBindingPattern(openBracket, new ArrayList<>(), member);
                STNode typeDesc = getTypeDescFromExpr(typeDescOrExpr);
                return STNodeFactory.createTypedBindingPatternNode(typeDesc, bindingPattern);
            case INDEXED_EXPRESSION:
                return parseAsMemberAccessExpr(typeDescOrExpr, openBracket, member);
            case NONE:
            default:
                
                
                break;
        }

        
        STNode memberEnd = parseBracketedListMemberEnd();
        if (memberEnd != null) {
            
            List<STNode> memberList = new ArrayList<>();
            memberList.add(member);
            memberList.add(memberEnd);
            STNode bindingPattern = parseAsListBindingPattern(openBracket, memberList);
            STNode typeDesc = getTypeDescFromExpr(typeDescOrExpr);
            return STNodeFactory.createTypedBindingPatternNode(typeDesc, bindingPattern);
        }

        
        
        
        
        
        STNode closeBracket = parseCloseBracket();
        endContext();
        return parseTypedBindingPatternOrMemberAccessRhs(typeDescOrExpr, openBracket, member, closeBracket,
                isTypedBindingPattern, context);
    }

    private STNode parseAsMemberAccessExpr(STNode typeNameOrExpr, STNode openBracket, STNode member) {
        STNode closeBracket = parseCloseBracket();
        endContext();
        STNode keyExpr = STNodeFactory.createNodeList(member);
        STNode memberAccessExpr =
                STNodeFactory.createIndexedExpressionNode(typeNameOrExpr, openBracket, keyExpr, closeBracket);
        return parseExpressionRhs(DEFAULT_OP_PRECEDENCE, memberAccessExpr, true, false);
    }

    private boolean isBracketedListEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACKET_TOKEN:
                return true;
            default:
                return false;
        }
    }

    private STNode parseBracketedListMember(boolean isTypedBindingPattern) {
        return parseBracketedListMember(peek().kind, isTypedBindingPattern);
    }

    /**
     * Parse a member of an ambiguous bracketed list. This member could be:
     * 1) Array length
     * 2) Key expression of a member-access-expr
     * 3) A member-binding pattern of a list-binding-pattern.
     *
     * @param nextTokenKind Kind of the next token
     * @param isTypedBindingPattern Is this in a definite typed-binding pattern
     * @return Parsed member node
     */
    private STNode parseBracketedListMember(SyntaxKind nextTokenKind, boolean isTypedBindingPattern) {
        switch (nextTokenKind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case ASTERISK_TOKEN:
                return parseBasicLiteral();
            case CLOSE_BRACKET_TOKEN:
                return STNodeFactory.createEmptyNode();
            case OPEN_BRACE_TOKEN:
            case ERROR_KEYWORD: 
            case ELLIPSIS_TOKEN: 
                return parseListBindingPatternMember();
            case IDENTIFIER_TOKEN:
                if (isTypedBindingPattern) {
                    STNode identifier = parseQualifiedIdentifier(ParserRuleContext.VARIABLE_REF);
                    nextTokenKind = peek().kind;
                    if (nextTokenKind == SyntaxKind.OPEN_PAREN_TOKEN) {
                        
                        return parseListBindingPatternMember();
                    }

                    return identifier;
                }
                break;
            default:
                if (!isTypedBindingPattern && isValidExpressionStart(nextTokenKind, 1)) {
                    break;
                }

                ParserRuleContext recoverContext =
                        isTypedBindingPattern ? ParserRuleContext.LIST_BINDING_MEMBER_OR_ARRAY_LENGTH
                                : ParserRuleContext.BRACKETED_LIST_MEMBER;
                Solution solution = recover(peek(), recoverContext, isTypedBindingPattern);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseBracketedListMember(solution.tokenKind, isTypedBindingPattern);
        }

        STNode expr = parseExpression();
        if (isWildcardBP(expr)) {
            return getWildcardBindingPattern(expr);
        }
        if (expr.kind == SyntaxKind.SIMPLE_NAME_REFERENCE || expr.kind == SyntaxKind.QUALIFIED_NAME_REFERENCE) {
            nextTokenKind = peek().kind;
            if (nextTokenKind == SyntaxKind.OPEN_PAREN_TOKEN) {
                
                return parseListBindingPatternMember();
            }
        }

        
        return expr;
    }

    /**
     * Treat the current node as an array, and parse the remainder of the binding pattern.
     *
     * @param typeDesc Type-desc
     * @param openBracket Open bracket
     * @param member Member
     * @return Parsed node
     */
    private STNode parseAsArrayTypeDesc(STNode typeDesc, STNode openBracket, STNode member, ParserRuleContext context) {
        
        
        typeDesc = getTypeDescFromExpr(typeDesc);
        STNode closeBracket = parseCloseBracket();
        endContext();
        return parseTypedBindingPatternOrMemberAccessRhs(typeDesc, openBracket, member, closeBracket, true, context);
    }

    private STNode parseBracketedListMemberEnd() {
        return parseBracketedListMemberEnd(peek().kind);
    }

    private STNode parseBracketedListMemberEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                return parseComma();
            case CLOSE_BRACKET_TOKEN:
                return null;
            default:
                Solution solution = recover(peek(), ParserRuleContext.BRACKETED_LIST_MEMBER_END);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseBracketedListMemberEnd(solution.tokenKind);
        }
    }

    /**
     * We reach here to break ambiguity of T[a]. This could be:
     * 1) Array Type Desc
     * 2) Member access on LHS
     * 3) Typed-binding-pattern
     *
     * @param typeDescOrExpr Type name or the expr that precede the open-bracket.
     * @param openBracket Open bracket
     * @param member Member
     * @param closeBracket Open bracket
     * @param isTypedBindingPattern Is this is a typed-binding-pattern.
     * @return Specific node that matches to T[a], after solving ambiguity.
     */
    private STNode parseTypedBindingPatternOrMemberAccessRhs(STNode typeDescOrExpr, STNode openBracket, STNode member,
                                                             STNode closeBracket, boolean isTypedBindingPattern,
                                                             ParserRuleContext context) {
        STToken nextToken = peek();
        return parseTypedBindingPatternOrMemberAccessRhs(nextToken.kind, typeDescOrExpr, openBracket, member,
                closeBracket, isTypedBindingPattern, context);
    }

    private STNode parseTypedBindingPatternOrMemberAccessRhs(SyntaxKind nextTokenKind, STNode typeDescOrExpr,
                                                             STNode openBracket, STNode member, STNode closeBracket,
                                                             boolean isTypedBindingPattern, ParserRuleContext context) {
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN: 
            case OPEN_BRACE_TOKEN: 
            case ERROR_KEYWORD: 
                
                STNode typeDesc = getTypeDescFromExpr(typeDescOrExpr);
                STNode arrayTypeDesc =
                        STNodeFactory.createArrayTypeDescriptorNode(typeDesc, openBracket, member, closeBracket);
                return parseTypedBindingPatternTypeRhs(arrayTypeDesc, context);
            case OPEN_BRACKET_TOKEN: 
                if (isTypedBindingPattern) {
                    typeDesc = getTypeDescFromExpr(typeDescOrExpr);
                    arrayTypeDesc =
                            STNodeFactory.createArrayTypeDescriptorNode(typeDesc, openBracket, member, closeBracket);
                    return parseTypedBindingPatternTypeRhs(arrayTypeDesc, context);
                }

                
                STNode keyExpr = STNodeFactory.createNodeList(member);
                STNode expr =
                        STNodeFactory.createIndexedExpressionNode(typeDescOrExpr, openBracket, keyExpr, closeBracket);
                return parseTypedBindingPatternOrMemberAccess(expr, false, context);
            case QUESTION_MARK_TOKEN:
                
                typeDesc = getTypeDescFromExpr(typeDescOrExpr);
                arrayTypeDesc =
                        STNodeFactory.createArrayTypeDescriptorNode(typeDesc, openBracket, member, closeBracket);
                typeDesc = parseComplexTypeDescriptor(arrayTypeDesc,
                        ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN, true);
                return parseTypedBindingPatternTypeRhs(typeDesc, context);
            case PIPE_TOKEN:
            case BITWISE_AND_TOKEN:
                
                return parseComplexTypeDescInTypedBindingPattern(typeDescOrExpr, openBracket, member, closeBracket,
                        context);
            case IN_KEYWORD:
                
                if (context != ParserRuleContext.FOREACH_STMT && context != ParserRuleContext.FROM_CLAUSE) {
                    break;
                }
                return createTypedBindingPattern(typeDescOrExpr, openBracket, member, closeBracket);
            case EQUAL_TOKEN: 
                if (context == ParserRuleContext.FOREACH_STMT || context == ParserRuleContext.FROM_CLAUSE) {
                    
                    
                    break;
                }

                if (isTypedBindingPattern) {
                    return createTypedBindingPattern(typeDescOrExpr, openBracket, member, closeBracket);
                }

                
                keyExpr = STNodeFactory.createNodeList(member);
                return STNodeFactory.createIndexedExpressionNode(typeDescOrExpr, openBracket, keyExpr, closeBracket);
            case SEMICOLON_TOKEN: 
                if (context == ParserRuleContext.FOREACH_STMT || context == ParserRuleContext.FROM_CLAUSE) {
                    
                    
                    break;
                }

                return createTypedBindingPattern(typeDescOrExpr, openBracket, member, closeBracket);
            case CLOSE_BRACE_TOKEN: 
            case COMMA_TOKEN:
                if (context == ParserRuleContext.AMBIGUOUS_STMT) {
                    keyExpr = STNodeFactory.createNodeList(member);
                    return STNodeFactory.createIndexedExpressionNode(typeDescOrExpr, openBracket, keyExpr,
                            closeBracket);
                }
                
            default:
                if (isValidExprRhsStart(nextTokenKind)) {
                    
                    keyExpr = STNodeFactory.createNodeList(member);
                    return STNodeFactory.createIndexedExpressionNode(typeDescOrExpr, openBracket, keyExpr,
                            closeBracket);
                }

                break;
        }

        Solution solution = recover(peek(), ParserRuleContext.BRACKETED_LIST_RHS, typeDescOrExpr, openBracket, member,
                closeBracket, true, context);

        
        
        
        if (solution.action == Action.REMOVE) {
            return solution.recoveredNode;
        }

        return parseTypedBindingPatternOrMemberAccessRhs(solution.tokenKind, typeDescOrExpr, openBracket, member,
                closeBracket, true, context);
    }

    private STNode createTypedBindingPattern(STNode typeDescOrExpr, STNode openBracket, STNode member,
                                             STNode closeBracket) {
        STNode bindingPatterns;
        if (isEmpty(member)) {
            bindingPatterns = STNodeFactory.createNodeList();
        } else {
            STNode varName = ((STSimpleNameReferenceNode) member).name;
            STNode bindingPattern = createCaptureOrWildcardBP(varName);
            bindingPatterns = STNodeFactory.createNodeList(bindingPattern);
        }

        STNode restBindingPattern = STNodeFactory.createEmptyNode();
        STNode bindingPattern = STNodeFactory.createListBindingPatternNode(openBracket, bindingPatterns,
                restBindingPattern, closeBracket);
        STNode typeDesc = getTypeDescFromExpr(typeDescOrExpr);
        return STNodeFactory.createTypedBindingPatternNode(typeDesc, bindingPattern);
    }

    /**
     * Parse a union or intersection type that involves array type-desc in lhs.
     * <p>
     * e.g: <code>(T[a] & R..)</code> or <code>(T[a] | R.. )</code>
     * <p>
     * Complexity occurs in scenarios such as <code>T[a] |/& R[b]</code>. If the token after this
     * is another binding-pattern, then <code>(T[a] |/& R[b])</code> becomes the type-desc. However,
     * if the token follows this is an equal or semicolon, then <code>(T[a] |/& R)</code> becomes
     * the type-desc, and <code>[b]</code> becomes the binding pattern.
     *
     * @param typeDescOrExpr Type desc or the expression
     * @param openBracket Open bracket
     * @param member Member
     * @param closeBracket Close bracket
     * @param context COntext in which the typed binding pattern occurs
     * @return Parsed node
     */
    private STNode parseComplexTypeDescInTypedBindingPattern(STNode typeDescOrExpr, STNode openBracket, STNode member,
                                                             STNode closeBracket, ParserRuleContext context) {
        
        
        STNode lhsTypeDesc = getTypeDescFromExpr(typeDescOrExpr);
        lhsTypeDesc = STNodeFactory.createArrayTypeDescriptorNode(lhsTypeDesc, openBracket, member, closeBracket);

        STNode pipeOrAndToken = parseUnionOrIntersectionToken();

        
        STTypedBindingPatternNode rhsTypedBindingPattern =
                (STTypedBindingPatternNode) parseTypedBindingPattern(context);
        STNode newTypeDesc;
        if (pipeOrAndToken.kind == SyntaxKind.PIPE_TOKEN) {
            newTypeDesc = STNodeFactory.createUnionTypeDescriptorNode(lhsTypeDesc, pipeOrAndToken,
                    rhsTypedBindingPattern.typeDescriptor);
        } else {
            newTypeDesc = STNodeFactory.createIntersectionTypeDescriptorNode(lhsTypeDesc, pipeOrAndToken,
                    rhsTypedBindingPattern.typeDescriptor);
        }

        return STNodeFactory.createTypedBindingPatternNode(newTypeDesc, rhsTypedBindingPattern.bindingPattern);
    }

    /**
     * Parse union (|) or intersection (&) type operator.
     *
     * @return pipe or bitwise and token
     */
    private STNode parseUnionOrIntersectionToken() {
        STToken token = peek();
        if (token.kind == SyntaxKind.PIPE_TOKEN || token.kind == SyntaxKind.BITWISE_AND_TOKEN) {
            return consume();
        } else {
            Solution sol = recover(token, ParserRuleContext.UNION_OR_INTERSECTION_TOKEN);
            return sol.recoveredNode;
        }
    }

    /**
     * Infer the type of the ambiguous bracketed list, based on the type of the member.
     *
     * @param memberNode Member node
     * @return Inferred type of the bracketed list
     */
    private SyntaxKind getBracketedListNodeType(STNode memberNode) {
        if (isEmpty(memberNode)) {
            
            return SyntaxKind.NONE;
        }
        if (memberNode.kind.compareTo(SyntaxKind.TYPE_DESC) >= 0 &&
                memberNode.kind.compareTo(SyntaxKind.SINGLETON_TYPE_DESC) <= 0) {
            return SyntaxKind.TUPLE_TYPE_DESC;
        }

        switch (memberNode.kind) {
            case ASTERISK_TOKEN:
                return SyntaxKind.ARRAY_TYPE_DESC;
            case CAPTURE_BINDING_PATTERN:
            case LIST_BINDING_PATTERN:
            case REST_BINDING_PATTERN:
            case MAPPING_BINDING_PATTERN:
            case WILDCARD_BINDING_PATTERN:
                return SyntaxKind.LIST_BINDING_PATTERN;
            case QUALIFIED_NAME_REFERENCE: 
            case REST_TYPE:
                return SyntaxKind.TUPLE_TYPE_DESC;
            case DECIMAL_INTEGER_LITERAL: 
            case HEX_INTEGER_LITERAL:
            case SIMPLE_NAME_REFERENCE: 
            case BRACKETED_LIST: 
                return SyntaxKind.NONE;
            default:
                return SyntaxKind.INDEXED_EXPRESSION;
        }
    }

    /**
     * Create a type-desc out of an expression.
     *
     * @param expression Expression
     * @return Type descriptor
     */
    private STNode getTypeDescFromExpr(STNode expression) {
        switch (expression.kind) {
            case INDEXED_EXPRESSION:
                break;
            case SIMPLE_NAME_REFERENCE:
            case QUALIFIED_NAME_REFERENCE:
            default:
                return expression;
        }

        STIndexedExpressionNode indexedExpr = (STIndexedExpressionNode) expression;
        STNode memberTypeDesc = getTypeDescFromExpr(indexedExpr.containerExpression);
        STNode arrayLength = getArrayLength((STNodeList) indexedExpr.keyExpression);
        return STNodeFactory.createArrayTypeDescriptorNode(memberTypeDesc, indexedExpr.openBracket, arrayLength,
                indexedExpr.closeBracket);
    }

    

    /*
     * This section tries to break the ambiguity in parsing a statement that starts with a open-bracket.
     * The ambiguity lies in between:
     * 1) Assignment that starts with list binding pattern
     * 2) Var-decl statement that starts with tuple type
     * 3) Statement that starts with list constructor, such as sync-send, etc.
     */

    /**
     * Parse any statement that starts with an open-bracket.
     *
     * @param annots Annotations attached to the statement.
     * @return Parsed node
     */
    private STNode parseStatementStartsWithOpenBracket(STNode annots, boolean possibleMappingField) {
        startContext(ParserRuleContext.ASSIGNMENT_OR_VAR_DECL_STMT);
        STNode bracketedList = parseStatementStartBracketedList(true);
        switch (bracketedList.kind) {
            case LIST_CONSTRUCTOR:
                switchContext(ParserRuleContext.EXPRESSION_STATEMENT);

                
                STListConstructorExpressionNode listConstructor = (STListConstructorExpressionNode) bracketedList;
                STNode members = listConstructor.expressions;
                if (possibleMappingField && peek().kind == SyntaxKind.COLON_TOKEN && members.bucketCount() == 1) {
                    startContext(ParserRuleContext.MAPPING_CONSTRUCTOR);
                    STNode colon = parseColon();
                    STNode fieldNameExpr = getExpression(members.childInBucket(0));
                    STNode valueExpr = parseExpression();
                    return STNodeFactory.createComputedNameFieldNode(listConstructor.openBracket, fieldNameExpr,
                            listConstructor.closeBracket, colon, valueExpr);
                }

                STNode expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, bracketedList, false, true);
                return parseStatementStartWithExpr(expr);
            case LIST_BINDING_PATTERN:
                switchContext(ParserRuleContext.ASSIGNMENT_STMT);
                return parseAssignmentStmtRhs(bracketedList);
            case TUPLE_TYPE_DESC:
            default:
                switchContext(ParserRuleContext.VAR_DECL_STMT);
                STNode varName = parseBindingPattern();
                STNode typedBindingPattern = STNodeFactory.createTypedBindingPatternNode(bracketedList, varName);
                return parseVarDeclRhs(annots, STNodeFactory.createEmptyNode(), typedBindingPattern, false);

        }
    }

    /**
     * The bracketed list at the start of a statement can be one of the following.
     * 1) List binding pattern
     * 2) Tuple type
     * 3) List constructor
     *
     * @param isRoot Is this the root of the list
     * @return Parsed node
     */
    private STNode parseStatementStartBracketedList(boolean isRoot) {
        startContext(ParserRuleContext.STMT_START_BRACKETED_LIST);
        STNode openBracket = parseOpenBracket();
        List<STNode> memberList = new ArrayList<>();
        while (!isBracketedListEnd(peek().kind)) {
            
            STNode member = parseStatementStartBracketedListMember();
            SyntaxKind currentNodeType = getStmtStartBracketedListType(member);

            switch (currentNodeType) {
                case TUPLE_TYPE_DESC:
                    
                    
                    return parseAsTupleTypeDesc(openBracket, memberList, member);
                case LIST_BINDING_PATTERN:
                    
                    
                    return parseAsListBindingPattern(openBracket, memberList, member);
                case LIST_CONSTRUCTOR:
                    
                    
                    return parseAsListConstructor(openBracket, memberList, member);
                case LIST_BP_OR_LIST_CONSTRUCTOR:
                    return parseAsListBindingPatternOrListConstructor(openBracket, memberList, member);
                case NONE:
                default:
                    memberList.add(member);
                    break;
            }

            
            STNode memberEnd = parseBracketedListMemberEnd();
            if (memberEnd == null) {
                break;
            }
            memberList.add(memberEnd);
        }

        
        STNode closeBracket = parseCloseBracket();
        STNode bracketedList = parseStatementStartBracketedList(openBracket, memberList, closeBracket, isRoot);
        endContext();
        return bracketedList;
    }

    private STNode parseStatementStartBracketedListMember() {
        return parseStatementStartBracketedListMember(peek().kind);
    }

    /**
     * Parse a member of a list-binding-pattern, tuple-type-desc, or
     * list-constructor-expr, when the parent is ambiguous.
     *
     * @param nextTokenKind Kind of the next token.
     * @return Parsed node
     */
    private STNode parseStatementStartBracketedListMember(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case OPEN_BRACKET_TOKEN:
                return parseStatementStartBracketedList(false);
            case IDENTIFIER_TOKEN:
                STNode identifier = parseQualifiedIdentifier(ParserRuleContext.VARIABLE_REF);
                STNode varName = ((STSimpleNameReferenceNode) identifier).name;
                if (isWildcardBP(varName)) {
                    return getWildcardBindingPattern(varName);
                }

                nextTokenKind = peek().kind;
                if (nextTokenKind == SyntaxKind.ELLIPSIS_TOKEN) {
                    STNode ellipsis = parseEllipsis();
                    return STNodeFactory.createRestDescriptorNode(identifier, ellipsis);
                }

                
                
                
                return parseExpressionRhs(DEFAULT_OP_PRECEDENCE, identifier, false, true);
            case OPEN_BRACE_TOKEN:
                
                return parseMappingBindingPatterOrMappingConstructor();
            case ERROR_KEYWORD:
                if (getNextNextToken(nextTokenKind).kind == SyntaxKind.OPEN_PAREN_TOKEN) {
                    return parseErrorConstructorExpr();
                }

                
                return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TUPLE);
            case ELLIPSIS_TOKEN:
                return parseListBindingPatternMember();
            case XML_KEYWORD:
            case STRING_KEYWORD:
                if (getNextNextToken(nextTokenKind).kind == SyntaxKind.BACKTICK_TOKEN) {
                    return parseExpression(false);
                }
                return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TUPLE);
            case TABLE_KEYWORD:
            case STREAM_KEYWORD:
                if (getNextNextToken(nextTokenKind).kind == SyntaxKind.LT_TOKEN) {
                    return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TUPLE);
                }
                return parseExpression(false);
            default:
                if (isValidExpressionStart(nextTokenKind, 1)) {
                    return parseExpression(false);
                }

                if (isTypeStartingToken(nextTokenKind)) {
                    return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TUPLE);
                }

                Solution solution = recover(peek(), ParserRuleContext.STMT_START_BRACKETED_LIST_MEMBER);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseStatementStartBracketedListMember(solution.tokenKind);
        }
    }

    private STNode parseAsTupleTypeDesc(STNode openBracket, List<STNode> memberList, STNode member) {
        memberList = getTypeDescList(memberList);
        switchContext(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
        startContext(ParserRuleContext.TYPE_DESC_IN_TUPLE);
        STNode tupleTypeMembers = parseTupleTypeMembers(member, memberList);
        STNode closeBracket = parseCloseBracket();
        endContext();
        endContext();
        return STNodeFactory.createTupleTypeDescriptorNode(openBracket, tupleTypeMembers, closeBracket);
    }

    private STNode parseAsListBindingPattern(STNode openBracket, List<STNode> memberList, STNode member) {
        memberList = getBindingPatternsList(memberList);
        memberList.add(member);
        switchContext(ParserRuleContext.LIST_BINDING_PATTERN);
        STNode listBindingPattern = parseListBindingPattern(openBracket, member, memberList);
        endContext();
        return listBindingPattern;
    }

    private STNode parseAsListBindingPattern(STNode openBracket, List<STNode> memberList) {
        memberList = getBindingPatternsList(memberList);
        switchContext(ParserRuleContext.LIST_BINDING_PATTERN);
        STNode listBindingPattern = parseListBindingPattern(openBracket, memberList);
        endContext();
        return listBindingPattern;
    }

    private STNode parseAsListBindingPatternOrListConstructor(STNode openBracket, List<STNode> memberList,
                                                              STNode member) {
        memberList.add(member);
        STNode memberEnd = parseBracketedListMemberEnd();

        STNode listBindingPatternOrListCons;
        if (memberEnd == null) {
            
            STNode closeBracket = parseCloseBracket();
            listBindingPatternOrListCons =
                    parseListBindingPatternOrListConstructor(openBracket, memberList, closeBracket);
        } else {
            memberList.add(memberEnd);
            listBindingPatternOrListCons = parseListBindingPatternOrListConstructor(openBracket, memberList);
        }

        return listBindingPatternOrListCons;
    }

    private SyntaxKind getStmtStartBracketedListType(STNode memberNode) {
        if (memberNode.kind.compareTo(SyntaxKind.TYPE_DESC) >= 0 &&
                memberNode.kind.compareTo(SyntaxKind.SINGLETON_TYPE_DESC) <= 0) {
            return SyntaxKind.TUPLE_TYPE_DESC;
        }

        switch (memberNode.kind) {
            case DECIMAL_INTEGER_LITERAL:
            case HEX_INTEGER_LITERAL:
            case ASTERISK_TOKEN:
                return SyntaxKind.ARRAY_TYPE_DESC;
            case CAPTURE_BINDING_PATTERN:
            case LIST_BINDING_PATTERN:
            case REST_BINDING_PATTERN:
            case WILDCARD_BINDING_PATTERN:
                return SyntaxKind.LIST_BINDING_PATTERN;
            case QUALIFIED_NAME_REFERENCE: 
            case REST_TYPE:
                return SyntaxKind.TUPLE_TYPE_DESC;
            case LIST_CONSTRUCTOR:
            case MAPPING_CONSTRUCTOR:
                return SyntaxKind.LIST_CONSTRUCTOR;
            case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
                
                return SyntaxKind.LIST_BP_OR_LIST_CONSTRUCTOR;
            case SIMPLE_NAME_REFERENCE: 
            case BRACKETED_LIST: 
            default:
                
                return SyntaxKind.NONE;
        }
    }

    private STNode parseStatementStartBracketedList(STNode openBracket, List<STNode> members, STNode closeBracket,
                                                    boolean isRoot) {
        if (!isRoot) {
            
            
            
            return new STAmbiguousCollectionNode(SyntaxKind.BRACKETED_LIST, openBracket, members, closeBracket);
        }

        switch (peek().kind) {
            case EQUAL_TOKEN:
                STNode memberBindingPatterns = STNodeFactory.createNodeList(getBindingPatternsList(members));
                STNode restBindingPattern = STNodeFactory.createEmptyNode();
                return STNodeFactory.createListBindingPatternNode(openBracket, memberBindingPatterns,
                        restBindingPattern, closeBracket);
            case IDENTIFIER_TOKEN:
            case OPEN_BRACKET_TOKEN:
            case OPEN_BRACE_TOKEN:
                if (members.isEmpty()) {
                    this.errorHandler.reportMissingTokenError("missing member");
                }
                STNode memberTypeDescs = STNodeFactory.createNodeList(getTypeDescList(members));
                return STNodeFactory.createTupleTypeDescriptorNode(openBracket, memberTypeDescs, closeBracket);
            case COLON_TOKEN: 
            default:
                STNode expressions = STNodeFactory.createNodeList(getExpressionList(members));
                return STNodeFactory.createListConstructorExpressionNode(openBracket, expressions, closeBracket);
        }
    }

    private boolean isWildcardBP(STNode node) {
        switch (node.kind) {
            case SIMPLE_NAME_REFERENCE:
                STToken nameToken = (STToken) ((STSimpleNameReferenceNode) node).name;
                return isUnderscoreToken(nameToken);
            case IDENTIFIER_TOKEN:
                return isUnderscoreToken((STToken) node);
            default:
                return false;

        }
    }

    private boolean isUnderscoreToken(STToken token) {
        return "_".equals(token.text());
    }

    private STNode getWildcardBindingPattern(STNode identifier) {
        switch (identifier.kind) {
            case SIMPLE_NAME_REFERENCE:
                STNode varName = ((STSimpleNameReferenceNode) identifier).name;
                return STNodeFactory.createWildcardBindingPatternNode(varName);
            case IDENTIFIER_TOKEN:
                return STNodeFactory.createWildcardBindingPatternNode(identifier);
            default:
                throw new IllegalStateException();
        }
    }

    

    /*
     * This section tries to break the ambiguity in parsing a statement that starts with a open-brace.
     */

    /**
     * Parse statements that starts with open-brace. It could be a:
     * 1) Block statement
     * 2) Var-decl with mapping binding pattern.
     * 3) Statement that starts with mapping constructor expression.
     *
     * @return Parsed node
     */
    private STNode parseStatementStartsWithOpenBrace() {
        startContext(ParserRuleContext.AMBIGUOUS_STMT);
        STNode openBrace = parseOpenBrace();
        if (peek().kind == SyntaxKind.CLOSE_BRACE_TOKEN) {
            STNode closeBrace = parseCloseBrace();
            switch (peek().kind) {
                case EQUAL_TOKEN:
                    switchContext(ParserRuleContext.ASSIGNMENT_STMT);
                    STNode fields = STNodeFactory.createEmptyNodeList();
                    STNode restBindingPattern = STNodeFactory.createEmptyNode();
                    STNode bindingPattern = STNodeFactory.createMappingBindingPatternNode(openBrace, fields,
                            restBindingPattern, closeBrace);
                    return parseAssignmentStmtRhs(bindingPattern);
                case RIGHT_ARROW_TOKEN:
                case SYNC_SEND_TOKEN:
                    switchContext(ParserRuleContext.EXPRESSION_STATEMENT);
                    fields = STNodeFactory.createEmptyNodeList();
                    STNode expr = STNodeFactory.createMappingConstructorExpressionNode(openBrace, fields, closeBrace);
                    expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, expr, false, true);
                    return parseStatementStartWithExpr(expr);
                default:
                    
                    STNode statements = STNodeFactory.createEmptyNodeList();
                    endContext();
                    return STNodeFactory.createBlockStatementNode(openBrace, statements, closeBrace);
            }
        }

        STNode member = parseStatementStartingBracedListFirstMember();
        SyntaxKind nodeType = getBracedListType(member);
        STNode stmt;
        switch (nodeType) {
            case MAPPING_BINDING_PATTERN:
                return parseStmtAsMappingBindingPatternStart(openBrace, member);
            case MAPPING_CONSTRUCTOR:
                return parseStmtAsMappingConstructorStart(openBrace, member);
            case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
                return parseStmtAsMappingBPOrMappingConsStart(openBrace, member);
            case BLOCK_STATEMENT:
                STNode closeBrace = parseCloseBrace();
                stmt = STNodeFactory.createBlockStatementNode(openBrace, member, closeBrace);
                endContext();
                return stmt;
            default:
                
                ArrayList<STNode> stmts = new ArrayList<>();
                stmts.add(member);
                STNode statements = parseStatements(stmts);
                closeBrace = parseCloseBrace();
                endContext();
                return STNodeFactory.createBlockStatementNode(openBrace, statements, closeBrace);
        }
    }

    /**
     * Parse the rest of the statement, treating the start as a mapping binding pattern.
     *
     * @param openBrace Open brace
     * @param firstMappingField First member
     * @return Parsed node
     */
    private STNode parseStmtAsMappingBindingPatternStart(STNode openBrace, STNode firstMappingField) {
        switchContext(ParserRuleContext.ASSIGNMENT_STMT);
        startContext(ParserRuleContext.MAPPING_BINDING_PATTERN);
        List<STNode> bindingPatterns = new ArrayList<>();
        if (firstMappingField.kind != SyntaxKind.REST_BINDING_PATTERN) {
            bindingPatterns.add(getBindingPattern(firstMappingField));
        }

        STNode mappingBP = parseMappingBindingPattern(openBrace, bindingPatterns, firstMappingField);
        return parseAssignmentStmtRhs(mappingBP);
    }

    /**
     * Parse the rest of the statement, treating the start as a mapping constructor expression.
     *
     * @param openBrace Open brace
     * @param firstMember First member
     * @return Parsed node
     */
    private STNode parseStmtAsMappingConstructorStart(STNode openBrace, STNode firstMember) {
        switchContext(ParserRuleContext.EXPRESSION_STATEMENT);
        List<STNode> members = new ArrayList<>();
        STNode mappingCons = parseAsMappingConstructor(openBrace, members, firstMember);

        
        STNode expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, mappingCons, false, true);
        return parseStatementStartWithExpr(expr);
    }

    /**
     * Parse the braced-list as a mapping constructor expression.
     *
     * @param openBrace Open brace
     * @param members members list
     * @param member Most recently parsed member
     * @return Parsed node
     */
    private STNode parseAsMappingConstructor(STNode openBrace, List<STNode> members, STNode member) {
        members.add(member);
        members = getExpressionList(members);

        
        switchContext(ParserRuleContext.MAPPING_CONSTRUCTOR);
        STNode fields = parseMappingConstructorFields(members);
        STNode closeBrace = parseCloseBrace();
        endContext();
        return STNodeFactory.createMappingConstructorExpressionNode(openBrace, fields, closeBrace);
    }

    /**
     * Parse the rest of the statement, treating the start as a mapping binding pattern
     * or a mapping constructor expression.
     *
     * @param openBrace Open brace
     * @param member First member
     * @return Parsed node
     */
    private STNode parseStmtAsMappingBPOrMappingConsStart(STNode openBrace, STNode member) {
        startContext(ParserRuleContext.MAPPING_BP_OR_MAPPING_CONSTRUCTOR);
        List<STNode> members = new ArrayList<>();
        members.add(member);

        STNode bpOrConstructor;
        STNode memberEnd = parseMappingFieldEnd();
        if (memberEnd == null) {
            STNode closeBrace = parseCloseBrace();
            
            bpOrConstructor = parseMappingBindingPatternOrMappingConstructor(openBrace, members, closeBrace);
        } else {
            members.add(memberEnd);
            bpOrConstructor = parseMappingBindingPatternOrMappingConstructor(openBrace, members);;
        }

        switch (bpOrConstructor.kind) {
            case MAPPING_CONSTRUCTOR:
                
                switchContext(ParserRuleContext.EXPRESSION_STATEMENT);
                STNode expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, bpOrConstructor, false, true);
                return parseStatementStartWithExpr(expr);
            case MAPPING_BINDING_PATTERN:
                switchContext(ParserRuleContext.ASSIGNMENT_STMT);
                STNode bindingPattern = getBindingPattern(bpOrConstructor);
                return parseAssignmentStmtRhs(bindingPattern);
            case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
            default:
                
                if (peek().kind == SyntaxKind.EQUAL_TOKEN) {
                    switchContext(ParserRuleContext.ASSIGNMENT_STMT);
                    bindingPattern = getBindingPattern(bpOrConstructor);
                    return parseAssignmentStmtRhs(bindingPattern);
                }

                
                switchContext(ParserRuleContext.EXPRESSION_STATEMENT);
                expr = getExpression(bpOrConstructor);
                expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, expr, false, true);
                return parseStatementStartWithExpr(expr);
        }
    }

    /**
     * Parse a member of a braced-list that occurs at the start of a statement.
     *
     * @return Parsed node
     */
    private STNode parseStatementStartingBracedListFirstMember() {
        STToken nextToken = peek();
        switch (nextToken.kind) {
            case IDENTIFIER_TOKEN:
                return parseIdentifierRhsInStmtStartingBrace();
            case STRING_LITERAL:
                STNode key = parseStringLiteral();
                if (peek().kind == SyntaxKind.COLON_TOKEN) {
                    STNode colon = parseColon();
                    STNode valueExpr = parseExpression();
                    return STNodeFactory.createSpecificFieldNode(key, colon, valueExpr);
                }

                STNode expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, key, false, true);
                return parseStatementStartWithExpr(expr);
            case OPEN_BRACKET_TOKEN:
                
                return parseStatementStartsWithOpenBracket(null, true);
            case OPEN_BRACE_TOKEN:
                return parseStatementStartsWithOpenBrace();
            case ELLIPSIS_TOKEN:
                return parseRestBindingPattern();
            default:
                
                switchContext(ParserRuleContext.BLOCK_STMT);
                return parseStatements();
        }
    }

    /**
     * Parse the rhs components of an identifier that follows an open brace,
     * at the start of a statement. i.e: "{foo".
     *
     * @return Parsed node
     */
    private STNode parseIdentifierRhsInStmtStartingBrace() {
        STNode identifier = parseIdentifier(ParserRuleContext.VARIABLE_REF);
        switch (peek().kind) {
            case COMMA_TOKEN: 
                
                STNode colon = STNodeFactory.createEmptyNode();
                STNode value = STNodeFactory.createEmptyNode();
                return STNodeFactory.createSpecificFieldNode(identifier, colon, value);
            case COLON_TOKEN:
                colon = parseColon();
                SyntaxKind nextTokenKind = peek().kind;
                switch (nextTokenKind) {
                    case OPEN_BRACKET_TOKEN: 
                        STNode bindingPatternOrExpr = parseListBindingPatternOrListConstructor();
                        return getMappingField(identifier, colon, bindingPatternOrExpr);
                    case OPEN_BRACE_TOKEN: 
                        bindingPatternOrExpr = parseMappingBindingPatterOrMappingConstructor();
                        return getMappingField(identifier, colon, bindingPatternOrExpr);
                    case IDENTIFIER_TOKEN: 
                        return parseQualifiedIdentifierRhsInStmtStartBrace(identifier, colon);
                    default:
                        STNode expr = parseExpression();
                        return getMappingField(identifier, colon, expr);
                }
            default:
                switchContext(ParserRuleContext.BLOCK_STMT);
                startContext(ParserRuleContext.AMBIGUOUS_STMT);
                STNode qualifiedIdentifier = parseQualifiedIdentifier(identifier);
                return parseStatementStartIdentifierRhs(null, qualifiedIdentifier);
        }
    }

    /**
     * Parse the rhs components of "<code>{ identifier : identifier</code>",
     * at the start of a statement. i.e: "{foo:bar".
     *
     * @return Parsed node
     */
    private STNode parseQualifiedIdentifierRhsInStmtStartBrace(STNode identifier, STNode colon) {
        STNode secondIdentifier = parseIdentifier(ParserRuleContext.VARIABLE_REF);
        STNode secondNameRef = STNodeFactory.createSimpleNameReferenceNode(secondIdentifier);
        if (isWildcardBP(secondIdentifier)) {
            
            return getWildcardBindingPattern(secondIdentifier);
        }

        
        SyntaxKind nextTokenKind = peek().kind;
        STNode qualifiedNameRef = STNodeFactory.createQualifiedNameReferenceNode(identifier, colon, secondNameRef);
        switch (nextTokenKind) {
            case COMMA_TOKEN:
                
                
                
                return qualifiedNameRef;
            case OPEN_BRACE_TOKEN: 
            case IDENTIFIER_TOKEN: 
                STNode finalKeyword = STNodeFactory.createEmptyNode();
                STNode typeBindingPattern =
                        parseTypedBindingPatternTypeRhs(qualifiedNameRef, ParserRuleContext.VAR_DECL_STMT);
                return parseVarDeclRhs(null, finalKeyword, typeBindingPattern, false);
            case OPEN_BRACKET_TOKEN:
                
                
                return parseMemberRhsInStmtStartWithBrace(identifier, colon, secondNameRef);
            case QUESTION_MARK_TOKEN:
                
                STNode typeDesc = parseComplexTypeDescriptor(qualifiedNameRef,
                        ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN, true);
                finalKeyword = STNodeFactory.createEmptyNode();
                typeBindingPattern = parseTypedBindingPatternTypeRhs(typeDesc, ParserRuleContext.VAR_DECL_STMT);
                return parseVarDeclRhs(null, finalKeyword, typeBindingPattern, false);
            case EQUAL_TOKEN:
            case SEMICOLON_TOKEN:
                
                return parseStatementStartWithExpr(qualifiedNameRef);
            case PIPE_TOKEN:
            case BITWISE_AND_TOKEN:
            default:
                return parseMemberWithExprInRhs(identifier, colon, secondNameRef, secondNameRef);
        }
    }

    private SyntaxKind getBracedListType(STNode member) {
        switch (member.kind) {
            case FIELD_BINDING_PATTERN:
            case CAPTURE_BINDING_PATTERN:
            case LIST_BINDING_PATTERN:
            case MAPPING_BINDING_PATTERN:
            case WILDCARD_BINDING_PATTERN:
                return SyntaxKind.MAPPING_BINDING_PATTERN;
            case SPECIFIC_FIELD:
                STNode expr = ((STSpecificFieldNode) member).valueExpr;
                
                if (expr == null || expr.kind == SyntaxKind.SIMPLE_NAME_REFERENCE ||
                        expr.kind == SyntaxKind.LIST_BP_OR_LIST_CONSTRUCTOR ||
                        expr.kind == SyntaxKind.MAPPING_BP_OR_MAPPING_CONSTRUCTOR) {
                    return SyntaxKind.MAPPING_BP_OR_MAPPING_CONSTRUCTOR;
                }
                return SyntaxKind.MAPPING_CONSTRUCTOR;
            case SPREAD_FIELD:
            case COMPUTED_NAME_FIELD:
                return SyntaxKind.MAPPING_CONSTRUCTOR;
            case SIMPLE_NAME_REFERENCE:
            case QUALIFIED_NAME_REFERENCE:
            case LIST_BP_OR_LIST_CONSTRUCTOR:
            case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
            case REST_BINDING_PATTERN:
                return SyntaxKind.MAPPING_BP_OR_MAPPING_CONSTRUCTOR;
            case LIST:
                return SyntaxKind.BLOCK_STATEMENT;
            default:
                return SyntaxKind.NONE;
        }
    }

    /**
     * Parse mapping binding pattern or mapping constructor.
     *
     * @return Parsed node
     */
    private STNode parseMappingBindingPatterOrMappingConstructor() {
        startContext(ParserRuleContext.MAPPING_BP_OR_MAPPING_CONSTRUCTOR);
        STNode openBrace = parseOpenBrace();
        List<STNode> memberList = new ArrayList<>();
        return parseMappingBindingPatternOrMappingConstructor(openBrace, memberList);
    }

    private boolean isBracedListEnd(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case EOF_TOKEN:
            case CLOSE_BRACE_TOKEN:
                return true;
            default:
                return false;
        }
    }

    private STNode parseMappingBindingPatternOrMappingConstructor(STNode openBrace, List<STNode> memberList) {
        STToken nextToken = peek();
        while (!isBracedListEnd(nextToken.kind)) {
            
            STNode member = parseMappingBindingPatterOrMappingConstructorMember(nextToken.kind);
            SyntaxKind currentNodeType = getTypeOfMappingBPOrMappingCons(member);

            switch (currentNodeType) {
                case MAPPING_CONSTRUCTOR:
                    
                    
                    return parseAsMappingConstructor(openBrace, memberList, member);
                case MAPPING_BINDING_PATTERN:
                    
                    
                    return parseAsMappingBindingPattern(openBrace, memberList, member);
                case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
                default:
                    memberList.add(member);
                    break;
            }

            
            STNode memberEnd = parseMappingFieldEnd();
            if (memberEnd == null) {
                break;
            }
            memberList.add(memberEnd);
            nextToken = peek();
        }

        
        STNode closeBrace = parseCloseBrace();
        return parseMappingBindingPatternOrMappingConstructor(openBrace, memberList, closeBrace);
    }

    private STNode parseMappingBindingPatterOrMappingConstructorMember(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case IDENTIFIER_TOKEN:
                STNode key = parseIdentifier(ParserRuleContext.MAPPING_FIELD_NAME);
                return parseMappingFieldRhs(key);
            case STRING_LITERAL:
                key = parseStringLiteral();
                STNode colon = parseColon();
                STNode valueExpr = parseExpression();
                return STNodeFactory.createSpecificFieldNode(key, colon, valueExpr);
            case OPEN_BRACKET_TOKEN:
                return parseComputedField();
            case ELLIPSIS_TOKEN:
                STNode ellipsis = parseEllipsis();
                STNode expr = parseExpression();
                if (expr.kind == SyntaxKind.SIMPLE_NAME_REFERENCE) {
                    return STNodeFactory.createRestBindingPatternNode(ellipsis, expr);
                }
                return STNodeFactory.createSpreadFieldNode(ellipsis, expr);
            default:
                Solution solution = recover(peek(), ParserRuleContext.MAPPING_BP_OR_MAPPING_CONSTRUCTOR_MEMBER);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseListBindingPatternOrListConstructorMember(solution.tokenKind);
        }
    }

    private STNode parseMappingFieldRhs(STNode key) {
        STToken nextToken = peek();
        return parseMappingFieldRhs(nextToken.kind, key);
    }

    private STNode parseMappingFieldRhs(SyntaxKind tokenKind, STNode key) {
        STNode colon;
        STNode valueExpr;
        switch (tokenKind) {
            case COLON_TOKEN:
                colon = parseColon();
                return parseMappingFieldValue(key, colon);
            case COMMA_TOKEN:
            case CLOSE_BRACE_TOKEN:
                colon = STNodeFactory.createEmptyNode();
                valueExpr = STNodeFactory.createEmptyNode();
                return STNodeFactory.createSpecificFieldNode(key, colon, valueExpr);
            default:
                STToken token = peek();
                Solution solution = recover(token, ParserRuleContext.SPECIFIC_FIELD_RHS, key);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseSpecificFieldRhs(solution.tokenKind, key);
        }
    }

    private STNode parseMappingFieldValue(STNode key, STNode colon) {
        
        STNode expr;
        switch (peek().kind) {
            case IDENTIFIER_TOKEN:
                expr = parseExpression();
                break;
            case OPEN_BRACKET_TOKEN: 
                expr = parseListBindingPatternOrListConstructor();
                break;
            case OPEN_BRACE_TOKEN: 
                expr = parseMappingBindingPatterOrMappingConstructor();
                break;
            default:
                expr = parseExpression();
                break;
        }

        return STNodeFactory.createSpecificFieldNode(key, colon, expr);
    }

    private SyntaxKind getTypeOfMappingBPOrMappingCons(STNode memberNode) {
        switch (memberNode.kind) {
            case FIELD_BINDING_PATTERN:
            case MAPPING_BINDING_PATTERN:
            case CAPTURE_BINDING_PATTERN:
            case LIST_BINDING_PATTERN:
            case WILDCARD_BINDING_PATTERN:
                return SyntaxKind.MAPPING_BINDING_PATTERN;
            case SPECIFIC_FIELD:
                STNode expr = ((STSpecificFieldNode) memberNode).valueExpr;
                
                if (expr == null || expr.kind == SyntaxKind.SIMPLE_NAME_REFERENCE ||
                        expr.kind == SyntaxKind.LIST_BP_OR_LIST_CONSTRUCTOR ||
                        expr.kind == SyntaxKind.MAPPING_BP_OR_MAPPING_CONSTRUCTOR) {
                    return SyntaxKind.MAPPING_BP_OR_MAPPING_CONSTRUCTOR;
                }
                return SyntaxKind.MAPPING_CONSTRUCTOR;
            case SPREAD_FIELD:
            case COMPUTED_NAME_FIELD:
                return SyntaxKind.MAPPING_CONSTRUCTOR;
            case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
            case SIMPLE_NAME_REFERENCE:
            case QUALIFIED_NAME_REFERENCE:
            case LIST_BP_OR_LIST_CONSTRUCTOR:
            case REST_BINDING_PATTERN: 
            default:
                return SyntaxKind.MAPPING_BP_OR_MAPPING_CONSTRUCTOR;
        }
    }

    private STNode parseMappingBindingPatternOrMappingConstructor(STNode openBrace, List<STNode> members,
                                                                  STNode closeBrace) {
        endContext();
        return new STAmbiguousCollectionNode(SyntaxKind.MAPPING_BP_OR_MAPPING_CONSTRUCTOR, openBrace, members,
                closeBrace);
    }

    private STNode parseAsMappingBindingPattern(STNode openBrace, List<STNode> members, STNode member) {
        members.add(member);
        members = getBindingPatternsList(members);
        
        switchContext(ParserRuleContext.MAPPING_BINDING_PATTERN);
        return parseMappingBindingPattern(openBrace, members, member);
    }

    /**
     * Parse list binding pattern or list constructor.
     *
     * @return Parsed node
     */
    private STNode parseListBindingPatternOrListConstructor() {
        startContext(ParserRuleContext.BRACKETED_LIST);
        STNode openBracket = parseOpenBracket();
        List<STNode> memberList = new ArrayList<>();
        return parseListBindingPatternOrListConstructor(openBracket, memberList);
    }

    private STNode parseListBindingPatternOrListConstructor(STNode openBracket, List<STNode> memberList) {
        
        STToken nextToken = peek();
        while (!isBracketedListEnd(nextToken.kind)) {
            
            STNode member = parseListBindingPatternOrListConstructorMember(nextToken.kind);
            SyntaxKind currentNodeType = getParsingNodeTypeOfListBPOrListCons(member);

            switch (currentNodeType) {
                case LIST_CONSTRUCTOR:
                    
                    
                    return parseAsListConstructor(openBracket, memberList, member);
                case LIST_BINDING_PATTERN:
                    
                    
                    return parseAsListBindingPattern(openBracket, memberList, member);
                case LIST_BP_OR_LIST_CONSTRUCTOR:
                default:
                    memberList.add(member);
                    break;
            }

            
            STNode memberEnd = parseBracketedListMemberEnd();
            if (memberEnd == null) {
                break;
            }
            memberList.add(memberEnd);
            nextToken = peek();
        }

        
        STNode closeBracket = parseCloseBracket();
        return parseListBindingPatternOrListConstructor(openBracket, memberList, closeBracket);
    }

    private STNode parseListBindingPatternOrListConstructorMember(SyntaxKind nextTokenKind) {
        switch (nextTokenKind) {
            case OPEN_BRACKET_TOKEN:
                
                return parseListBindingPatternOrListConstructor();
            case IDENTIFIER_TOKEN:
                STNode identifier = parseQualifiedIdentifier(ParserRuleContext.VARIABLE_REF);
                if (isWildcardBP(identifier)) {
                    return getWildcardBindingPattern(identifier);
                }

                
                
                return parseExpressionRhs(DEFAULT_OP_PRECEDENCE, identifier, false, false);
            case OPEN_BRACE_TOKEN:
                return parseMappingBindingPatterOrMappingConstructor();
            case ELLIPSIS_TOKEN:
                return parseListBindingPatternMember();
            default:
                if (isValidExpressionStart(nextTokenKind, 1)) {
                    return parseExpression();
                }

                Solution solution = recover(peek(), ParserRuleContext.LIST_BP_OR_LIST_CONSTRUCTOR_MEMBER);

                
                
                
                if (solution.action == Action.REMOVE) {
                    return solution.recoveredNode;
                }

                return parseListBindingPatternOrListConstructorMember(solution.tokenKind);
        }
    }

    private SyntaxKind getParsingNodeTypeOfListBPOrListCons(STNode memberNode) {
        switch (memberNode.kind) {
            case CAPTURE_BINDING_PATTERN:
            case LIST_BINDING_PATTERN:
            case REST_BINDING_PATTERN:
            case MAPPING_BINDING_PATTERN:
            case WILDCARD_BINDING_PATTERN:
                return SyntaxKind.LIST_BINDING_PATTERN;
            case SIMPLE_NAME_REFERENCE: 
            case LIST_BP_OR_LIST_CONSTRUCTOR: 
                return SyntaxKind.LIST_BP_OR_LIST_CONSTRUCTOR;
            default:
                return SyntaxKind.LIST_CONSTRUCTOR;
        }
    }

    private STNode parseAsListConstructor(STNode openBracket, List<STNode> memberList, STNode member) {
        memberList.add(member);
        memberList = getExpressionList(memberList);

        switchContext(ParserRuleContext.LIST_CONSTRUCTOR);
        STNode expressions = parseOptionalExpressionsList(memberList);
        STNode closeBracket = parseCloseBracket();
        endContext();
        return STNodeFactory.createListConstructorExpressionNode(openBracket, expressions, closeBracket);
    }

    private STNode parseListBindingPatternOrListConstructor(STNode openBracket, List<STNode> members,
                                                            STNode closeBracket) {
        STNode lbpOrListCons;
        switch (peek().kind) {
            case COMMA_TOKEN: 
            case CLOSE_BRACE_TOKEN: 
            case CLOSE_BRACKET_TOKEN:
                lbpOrListCons = new STAmbiguousCollectionNode(SyntaxKind.LIST_BP_OR_LIST_CONSTRUCTOR, openBracket,
                        members, closeBracket);
                break;
            default:
                if (isValidExprRhsStart(peek().kind)) {
                    members = getExpressionList(members);
                    STNode memberExpressions = STNodeFactory.createNodeList(members);
                    lbpOrListCons = STNodeFactory.createListConstructorExpressionNode(openBracket, memberExpressions,
                            closeBracket);
                    break;
                }

                
                members = getBindingPatternsList(members);
                STNode bindingPatternsNode = STNodeFactory.createNodeList(members);
                STNode restBindingPattern = STNodeFactory.createEmptyNode();
                lbpOrListCons = STNodeFactory.createListBindingPatternNode(openBracket, bindingPatternsNode,
                        restBindingPattern, closeBracket);
                break;
        }

        endContext();
        return lbpOrListCons;
    }

    private STNode parseMemberRhsInStmtStartWithBrace(STNode identifier, STNode colon, STNode secondIdentifier) {
        STNode typedBPOrExpr =
                parseTypedBindingPatternOrMemberAccess(secondIdentifier, false, ParserRuleContext.AMBIGUOUS_STMT);
        if (typedBPOrExpr.kind == SyntaxKind.INDEXED_EXPRESSION) {
            return parseMemberWithExprInRhs(identifier, colon, secondIdentifier, typedBPOrExpr);
        }

        switchContext(ParserRuleContext.BLOCK_STMT);
        startContext(ParserRuleContext.VAR_DECL_STMT);
        STNode finalKeyword = STNodeFactory.createEmptyNode();
        STNode annots = STNodeFactory.createEmptyNode();

        
        
        
        STNode qualifiedNameRef = STNodeFactory.createQualifiedNameReferenceNode(identifier, colon, secondIdentifier);
        STNode typeDesc = mergeQualifiedNameWithTypeDesc(qualifiedNameRef,
                ((STTypedBindingPatternNode) typedBPOrExpr).typeDescriptor);
        return parseVarDeclRhs(annots, finalKeyword, typeDesc, false);
    }

    /**
     * Parse a member that starts with "foo:bar[", in a statement starting with a brace.
     *
     * @param identifier First identifier of the statement
     * @param colon Colon that follows the first identifier
     * @param secondIdentifier Identifier that follows the colon
     * @param memberAccessExpr Member access expression
     * @return Parsed node
     */
    private STNode parseMemberWithExprInRhs(STNode identifier, STNode colon, STNode secondIdentifier,
                                            STNode memberAccessExpr) {
        STNode expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, memberAccessExpr, false, true);
        switch (peek().kind) {
            case COMMA_TOKEN:
            case CLOSE_BRACE_TOKEN:
                switchContext(ParserRuleContext.EXPRESSION_STATEMENT);
                startContext(ParserRuleContext.MAPPING_CONSTRUCTOR);
                return STNodeFactory.createSpecificFieldNode(identifier, colon, expr);
            case EQUAL_TOKEN:
            case SEMICOLON_TOKEN:
            default:
                switchContext(ParserRuleContext.BLOCK_STMT);
                startContext(ParserRuleContext.EXPRESSION_STATEMENT);
                
                STNode qualifiedName =
                        STNodeFactory.createQualifiedNameReferenceNode(identifier, colon, secondIdentifier);
                STNode updatedExpr = mergeQualifiedNameWithExpr(qualifiedName, expr);
                return parseStatementStartWithExpr(updatedExpr);
        }
    }

    /**
     * Replace the first identifier of an expression, with a given qualified-identifier.
     * Only expressions that can start with "bar[..]" can reach here.
     *
     * @param qualifiedName Qualified identifier to replace simple identifier
     * @param exprOrAction Expression or action
     * @return Updated expression
     */
    private STNode mergeQualifiedNameWithExpr(STNode qualifiedName, STNode exprOrAction) {
        switch (exprOrAction.kind) {
            case SIMPLE_NAME_REFERENCE:
                return qualifiedName;
            case BINARY_EXPRESSION:
                STBinaryExpressionNode binaryExpr = (STBinaryExpressionNode) exprOrAction;
                STNode newLhsExpr = mergeQualifiedNameWithExpr(qualifiedName, binaryExpr.lhsExpr);
                return STNodeFactory.createBinaryExpressionNode(binaryExpr.kind, newLhsExpr, binaryExpr.operator,
                        binaryExpr.rhsExpr);
            case FIELD_ACCESS:
                STFieldAccessExpressionNode fieldAccess = (STFieldAccessExpressionNode) exprOrAction;
                newLhsExpr = mergeQualifiedNameWithExpr(qualifiedName, fieldAccess.expression);
                return STNodeFactory.createFieldAccessExpressionNode(newLhsExpr, fieldAccess.dotToken,
                        fieldAccess.fieldName);
            case INDEXED_EXPRESSION:
                STIndexedExpressionNode memberAccess = (STIndexedExpressionNode) exprOrAction;
                newLhsExpr = mergeQualifiedNameWithExpr(qualifiedName, memberAccess.containerExpression);
                return STNodeFactory.createIndexedExpressionNode(newLhsExpr, memberAccess.openBracket,
                        memberAccess.keyExpression, memberAccess.closeBracket);
            case TYPE_TEST_EXPRESSION:
                STTypeTestExpressionNode typeTest = (STTypeTestExpressionNode) exprOrAction;
                newLhsExpr = mergeQualifiedNameWithExpr(qualifiedName, typeTest.expression);
                return STNodeFactory.createTypeTestExpressionNode(newLhsExpr, typeTest.isKeyword,
                        typeTest.typeDescriptor);
            case ANNOT_ACCESS:
                STAnnotAccessExpressionNode annotAccess = (STAnnotAccessExpressionNode) exprOrAction;
                newLhsExpr = mergeQualifiedNameWithExpr(qualifiedName, annotAccess.expression);
                return STNodeFactory.createFieldAccessExpressionNode(newLhsExpr, annotAccess.annotChainingToken,
                        annotAccess.annotTagReference);
            case OPTIONAL_FIELD_ACCESS:
                STOptionalFieldAccessExpressionNode optionalFieldAccess =
                        (STOptionalFieldAccessExpressionNode) exprOrAction;
                newLhsExpr = mergeQualifiedNameWithExpr(qualifiedName, optionalFieldAccess.expression);
                return STNodeFactory.createFieldAccessExpressionNode(newLhsExpr,
                        optionalFieldAccess.optionalChainingToken, optionalFieldAccess.fieldName);
            case CONDITIONAL_EXPRESSION:
                STConditionalExpressionNode conditionalExpr = (STConditionalExpressionNode) exprOrAction;
                newLhsExpr = mergeQualifiedNameWithExpr(qualifiedName, conditionalExpr.lhsExpression);
                return STNodeFactory.createConditionalExpressionNode(newLhsExpr, conditionalExpr.questionMarkToken,
                        conditionalExpr.middleExpression, conditionalExpr.colonToken, conditionalExpr.endExpression);
            case REMOTE_METHOD_CALL_ACTION:
                STRemoteMethodCallActionNode remoteCall = (STRemoteMethodCallActionNode) exprOrAction;
                newLhsExpr = mergeQualifiedNameWithExpr(qualifiedName, remoteCall.expression);
                return STNodeFactory.createRemoteMethodCallActionNode(newLhsExpr, remoteCall.rightArrowToken,
                        remoteCall.methodName, remoteCall.openParenToken, remoteCall.arguments,
                        remoteCall.closeParenToken);
            case ASYNC_SEND_ACTION:
                STAsyncSendActionNode asyncSend = (STAsyncSendActionNode) exprOrAction;
                newLhsExpr = mergeQualifiedNameWithExpr(qualifiedName, asyncSend.expression);
                return STNodeFactory.createAsyncSendActionNode(newLhsExpr, asyncSend.rightArrowToken,
                        asyncSend.peerWorker);
            case SYNC_SEND_ACTION:
                STSyncSendActionNode syncSend = (STSyncSendActionNode) exprOrAction;
                newLhsExpr = mergeQualifiedNameWithExpr(qualifiedName, syncSend.expression);
                return STNodeFactory.createAsyncSendActionNode(newLhsExpr, syncSend.syncSendToken, syncSend.peerWorker);
            default:
                return exprOrAction;
        }
    }

    private STNode mergeQualifiedNameWithTypeDesc(STNode qualifiedName, STNode typeDesc) {
        switch (typeDesc.kind) {
            case SIMPLE_NAME_REFERENCE:
                return qualifiedName;
            case ARRAY_TYPE_DESC:
                STArrayTypeDescriptorNode arrayTypeDesc = (STArrayTypeDescriptorNode) typeDesc;
                STNode newMemberType = mergeQualifiedNameWithTypeDesc(qualifiedName, arrayTypeDesc.memberTypeDesc);
                return STNodeFactory.createArrayTypeDescriptorNode(newMemberType, arrayTypeDesc.openBracket,
                        arrayTypeDesc.arrayLength, arrayTypeDesc.closeBracket);
            case UNION_TYPE_DESC:
                STUnionTypeDescriptorNode unionTypeDesc = (STUnionTypeDescriptorNode) typeDesc;
                STNode newlhsType = mergeQualifiedNameWithTypeDesc(qualifiedName, unionTypeDesc.leftTypeDesc);
                return STNodeFactory.createUnionTypeDescriptorNode(newlhsType, unionTypeDesc.pipeToken,
                        unionTypeDesc.rightTypeDesc);
            case INTERSECTION_TYPE_DESC:
                STIntersectionTypeDescriptorNode intersectionTypeDesc = (STIntersectionTypeDescriptorNode) typeDesc;
                newlhsType = mergeQualifiedNameWithTypeDesc(qualifiedName, intersectionTypeDesc.leftTypeDesc);
                return STNodeFactory.createUnionTypeDescriptorNode(newlhsType, intersectionTypeDesc.bitwiseAndToken,
                        intersectionTypeDesc.rightTypeDesc);
            case OPTIONAL_TYPE_DESC:
                STOptionalTypeDescriptorNode optionalType = (STOptionalTypeDescriptorNode) typeDesc;
                newMemberType = mergeQualifiedNameWithTypeDesc(qualifiedName, optionalType.typeDescriptor);
                return STNodeFactory.createOptionalTypeDescriptorNode(newMemberType, optionalType.questionMarkToken);
            default:
                return typeDesc;
        }
    }

    

    private List<STNode> getTypeDescList(List<STNode> ambibuousList) {
        List<STNode> typeDescList = new ArrayList<STNode>();
        for (STNode item : ambibuousList) {
            
            
            
            if (item.kind != SyntaxKind.BRACKETED_LIST) {
                typeDescList.add(item);
                continue;
            }

            STAmbiguousCollectionNode innerList = (STAmbiguousCollectionNode) item;
            STNode memberTypeDescList = STNodeFactory.createNodeList(getTypeDescList(innerList.members));
            STNode typeDesc = STNodeFactory.createTupleTypeDescriptorNode(innerList.collectionStartToken,
                    memberTypeDescList, innerList.collectionEndToken);
            typeDescList.add(typeDesc);
        }

        return typeDescList;
    }

    private List<STNode> getBindingPatternsList(List<STNode> ambibuousList) {
        List<STNode> bindingPatterns = new ArrayList<STNode>();
        for (STNode item : ambibuousList) {
            bindingPatterns.add(getBindingPattern(item));
        }
        return bindingPatterns;
    }

    private STNode getBindingPattern(STNode ambiguousNode) {
        if (isEmpty(ambiguousNode)) {
            return ambiguousNode;
        }

        switch (ambiguousNode.kind) {
            case SIMPLE_NAME_REFERENCE:
                STNode varName = ((STSimpleNameReferenceNode) ambiguousNode).name;
                return createCaptureOrWildcardBP(varName);
            case QUALIFIED_NAME_REFERENCE:
                STQualifiedNameReferenceNode qualifiedName = (STQualifiedNameReferenceNode) ambiguousNode;
                return STNodeFactory.createFieldBindingPatternFullNode(qualifiedName.modulePrefix, qualifiedName.colon,
                        getBindingPattern(qualifiedName.identifier));
            case BRACKETED_LIST:
            case LIST_BP_OR_LIST_CONSTRUCTOR:
                STAmbiguousCollectionNode innerList = (STAmbiguousCollectionNode) ambiguousNode;
                STNode memberBindingPatterns = STNodeFactory.createNodeList(getBindingPatternsList(innerList.members));
                STNode restBindingPattern = STNodeFactory.createEmptyNode();
                return STNodeFactory.createListBindingPatternNode(innerList.collectionStartToken, memberBindingPatterns,
                        restBindingPattern, innerList.collectionEndToken);
            case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
                innerList = (STAmbiguousCollectionNode) ambiguousNode;
                memberBindingPatterns = STNodeFactory.createNodeList(getBindingPatternsList(innerList.members));
                restBindingPattern = STNodeFactory.createEmptyNode();
                return STNodeFactory.createMappingBindingPatternNode(innerList.collectionStartToken,
                        memberBindingPatterns, restBindingPattern, innerList.collectionEndToken);
            case SPECIFIC_FIELD:
                STSpecificFieldNode field = (STSpecificFieldNode) ambiguousNode;
                return STNodeFactory.createFieldBindingPatternFullNode(field.fieldName, field.colon,
                        getBindingPattern(field.valueExpr));
            default:
                return ambiguousNode;
        }
    }

    private List<STNode> getExpressionList(List<STNode> ambibuousList) {
        List<STNode> exprList = new ArrayList<STNode>();
        for (STNode item : ambibuousList) {
            exprList.add(getExpression(item));
        }
        return exprList;
    }

    private STNode getExpression(STNode ambiguousNode) {
        if (isEmpty(ambiguousNode)) {
            return ambiguousNode;
        }

        switch (ambiguousNode.kind) {
            case BRACKETED_LIST:
            case LIST_BP_OR_LIST_CONSTRUCTOR:
                STAmbiguousCollectionNode innerList = (STAmbiguousCollectionNode) ambiguousNode;
                STNode memberExprs = STNodeFactory.createNodeList(getExpressionList(innerList.members));
                return STNodeFactory.createListConstructorExpressionNode(innerList.collectionStartToken, memberExprs,
                        innerList.collectionEndToken);
            case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
                innerList = (STAmbiguousCollectionNode) ambiguousNode;
                memberExprs = STNodeFactory.createNodeList(getExpressionList(innerList.members));
                return STNodeFactory.createMappingConstructorExpressionNode(innerList.collectionStartToken, memberExprs,
                        innerList.collectionEndToken);
            case REST_BINDING_PATTERN:
                STRestBindingPatternNode restBindingPattern = (STRestBindingPatternNode) ambiguousNode;
                return STNodeFactory.createSpreadFieldNode(restBindingPattern.ellipsisToken,
                        restBindingPattern.variableName);
            case SPECIFIC_FIELD:
                
                STSpecificFieldNode field = (STSpecificFieldNode) ambiguousNode;
                return STNodeFactory.createSpecificFieldNode(field.fieldName, field.colon,
                        getExpression(field.valueExpr));
            case SIMPLE_NAME_REFERENCE:
            case QUALIFIED_NAME_REFERENCE:
            default:
                return ambiguousNode;
        }
    }

    private STNode getMappingField(STNode identifier, STNode colon, STNode bindingPatternOrExpr) {
        STNode simpleNameRef = STNodeFactory.createSimpleNameReferenceNode(identifier);
        switch (bindingPatternOrExpr.kind) {
            case LIST_BINDING_PATTERN:
            case MAPPING_BINDING_PATTERN:
                return STNodeFactory.createFieldBindingPatternFullNode(simpleNameRef, colon, bindingPatternOrExpr);
            case LIST_CONSTRUCTOR:
            case MAPPING_CONSTRUCTOR:
                return STNodeFactory.createSpecificFieldNode(simpleNameRef, colon, identifier);
            case LIST_BP_OR_LIST_CONSTRUCTOR:
            case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
            default:
                
                
                switchContext(ParserRuleContext.EXPRESSION_STATEMENT);
                startContext(ParserRuleContext.MAPPING_CONSTRUCTOR);
                return STNodeFactory.createSpecificFieldNode(identifier, colon, bindingPatternOrExpr);
        }
    }

    

    
    
}