Code review comment: I see your point. +1 for the current approach then.

Code before 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.
* <p>
* <code>
* listener-decl := metadata [public] listener [type-descriptor] variable-name = expression ;
* </code>
*
* @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();
if (peek().kind == SyntaxKind.IDENTIFIER_TOKEN) {
STNode listenerDecl =
parseConstantOrListenerDeclWithOptionalType(metadata, qualifier, listenerKeyword, true);
endContext();
return listenerDecl;
}
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 keyword) {
switch (nextTokenKind) {
case ANNOTATION_KEYWORD:
switchContext(ParserRuleContext.ANNOTATION_DECL);
return parseAnnotationDeclaration(metadata, qualifier, keyword);
case IDENTIFIER_TOKEN:
return parseConstantOrListenerDeclWithOptionalType(metadata, qualifier, keyword, false);
default:
if (isTypeStartingToken(nextTokenKind)) {
break;
}
STToken token = peek();
Solution solution = recover(token, ParserRuleContext.CONST_DECL_TYPE, metadata, qualifier, keyword);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseConstDeclFromType(solution.tokenKind, metadata, qualifier, keyword);
}
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, keyword, typeDesc, variableName,
equalsToken, initializer, semicolonToken);
}
private STNode parseConstantOrListenerDeclWithOptionalType(STNode metadata, STNode qualifier, STNode constKeyword,
boolean isListener) {
STNode varNameOrTypeName = parseStatementStartIdentifier();
STNode constDecl =
parseConstantOrListenerDeclRhs(metadata, qualifier, constKeyword, varNameOrTypeName, isListener);
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 keyword Keyword
* @param typeOrVarName Identifier that follows the const-keywoord
* @return Parsed node
*/
private STNode parseConstantOrListenerDeclRhs(STNode metadata, STNode qualifier, STNode keyword,
STNode typeOrVarName, boolean isListener) {
if (typeOrVarName.kind == SyntaxKind.QUALIFIED_NAME_REFERENCE) {
STNode type = typeOrVarName;
STNode variableName = parseVariableName();
return parseListenerOrConstRhs(metadata, qualifier, keyword, isListener, type, variableName);
}
STToken token = peek();
return parseConstantOrListenerDeclRhs(token.kind, metadata, qualifier, keyword, typeOrVarName, isListener);
}
private STNode parseConstantOrListenerDeclRhs(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
STNode keyword, STNode typeOrVarName, boolean isListener) {
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, keyword,
typeOrVarName, isListener);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseConstantOrListenerDeclRhs(solution.tokenKind, metadata, qualifier, keyword, typeOrVarName,
isListener);
}
return parseListenerOrConstRhs(metadata, qualifier, keyword, isListener, type, variableName);
}
private STNode parseListenerOrConstRhs(STNode metadata, STNode qualifier, STNode keyword, boolean isListener,
STNode type, STNode variableName) {
STNode equalsToken = parseAssignOp();
STNode initializer = parseExpression();
STNode semicolonToken = parseSemicolon();
if (isListener) {
return STNodeFactory.createListenerDeclarationNode(metadata, qualifier, keyword, type, variableName,
equalsToken, initializer, semicolonToken);
}
return STNodeFactory.createConstantDeclarationNode(metadata, qualifier, keyword, 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, boolean isInConditionalExpr) {
STNode typeofKeyword = parseTypeofKeyword();
STNode expr = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false, isInConditionalExpr);
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();
typeDescriptorNode = validateForUsageOfVar(typeDescriptorNode);
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, boolean isInConditionalExpr) {
STNode unaryOperator = parseUnaryOperator();
STNode expr = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false, isInConditionalExpr);
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 createArrayTypeDesc(memberTypeDesc, openBracketToken, arrayLengthNode, closeBracketToken);
}
private STNode createArrayTypeDesc(STNode memberTypeDesc, STNode openBracketToken, STNode arrayLengthNode,
STNode closeBracketToken) {
memberTypeDesc = validateForUsageOfVar(memberTypeDesc);
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 parseOptionalAnnotations() {
STToken nextToken = peek();
return parseOptionalAnnotations(nextToken.kind);
}
private STNode parseOptionalAnnotations(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 list with atleast one annotation.
*
* @return Annotation list
*/
private STNode parseAnnotations() {
startContext(ParserRuleContext.ANNOTATIONS);
List<STNode> annotList = new ArrayList<>();
annotList.add(parseAnnotation());
while (peek().kind == SyntaxKind.AT_TOKEN) {
annotList.add(parseAnnotation());
}
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_STRING:
docString = parseMarkdownDocumentation();
annotations = parseOptionalAnnotations();
break;
case AT_TOKEN:
docString = STNodeFactory.createEmptyNode();
annotations = parseOptionalAnnotations(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.createEmptyNodeList());
}
/**
* 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, boolean isInConditionalExpr) {
STNode isKeyword = parseIsKeyword();
STNode typeDescriptor =
parseTypeDescriptorInExpression(ParserRuleContext.TYPE_DESC_IN_EXPRESSION, isInConditionalExpr);
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);
}
/**
* Parse statement which is only consists of an action or expression.
*
* @param annots Annotations
* @param nextTokenKind Next token kind
* @return Statement node
*/
private STNode parseExpressionStatement(SyntaxKind nextTokenKind, STNode annots) {
startContext(ParserRuleContext.EXPRESSION_STATEMENT);
STNode expression = parseActionOrExpressionInLhs(nextTokenKind, annots);
return getExpressionAsStatement(expression);
}
/**
* Parse statements that starts with an expression.
*
* @return Statement node
*/
private STNode parseStatementStartWithExpr(STNode annots) {
startContext(ParserRuleContext.AMBIGUOUS_STMT);
STNode expr = parseActionOrExpressionInLhs(peek().kind, annots);
return parseStatementStartWithExprRhs(expr);
}
/**
* Parse rhs of statements that starts with an expression.
*
* @return Statement node
*/
private STNode parseStatementStartWithExprRhs(STNode expression) {
STToken nextToken = peek();
return parseStatementStartWithExprRhs(nextToken.kind, expression);
}
/**
* Parse the component followed by the expression, at the beginning of a statement.
*
* @param nextTokenKind Kind of the next token
* @return Statement node
*/
private STNode parseStatementStartWithExprRhs(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);
}
ParserRuleContext context;
if (isPossibleExpressionStatement(expression)) {
context = ParserRuleContext.EXPR_STMT_RHS;
} else {
context = ParserRuleContext.STMT_START_WITH_EXPR_RHS;
}
Solution solution = recover(peek(), context, expression);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseStatementStartWithExprRhs(solution.tokenKind, expression);
}
}
private boolean isPossibleExpressionStatement(STNode expression) {
switch (expression.kind) {
case METHOD_CALL:
case FUNCTION_CALL:
case CHECK_EXPRESSION:
case FAIL_EXPRESSION:
case REMOTE_METHOD_CALL_ACTION:
case CHECK_ACTION:
case FAIL_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 true;
default:
return false;
}
}
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 FAIL_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:
case FAIL_EXPRESSION:
return parseActionStatement(expression);
default:
STNode semicolon = parseSemicolon();
endContext();
STNode exprStmt = STNodeFactory.createExpressionStatementNode(SyntaxKind.INVALID_EXPRESSION_STATEMENT,
expression, semicolon);
exprStmt = SyntaxErrors.addDiagnostic(exprStmt, DiagnosticErrorCode.ERROR_INVALID_EXPRESSION_STATEMENT);
return exprStmt;
}
}
private STNode parseArrayTypeDescriptorNode(STIndexedExpressionNode indexedExpr) {
STNode memberTypeDesc = getTypeDescFromExpr(indexedExpr.containerExpression);
STNodeList lengthExprs = (STNodeList) indexedExpr.keyExpression;
if (lengthExprs.isEmpty()) {
return createArrayTypeDesc(memberTypeDesc, indexedExpr.openBracket, STNodeFactory.createEmptyNode(),
indexedExpr.closeBracket);
}
STNode lengthExpr = lengthExprs.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:
STNode newOpenBracketWithDiagnostics = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(
indexedExpr.openBracket, lengthExpr, DiagnosticErrorCode.ERROR_INVALID_ARRAY_LENGTH);
indexedExpr = indexedExpr.replace(indexedExpr.openBracket, newOpenBracketWithDiagnostics);
lengthExpr = STNodeFactory.createEmptyNode();
}
return createArrayTypeDesc(memberTypeDesc, indexedExpr.openBracket, lengthExpr, indexedExpr.closeBracket);
}
/**
* <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) {
STNode semicolon = parseSemicolon();
endContext();
return STNodeFactory.createExpressionStatementNode(SyntaxKind.CALL_STATEMENT, expression, semicolon);
}
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(expression, isRhsExpr, rightArrow);
}
private STNode parseRemoteCallOrAsyncSendActionRhs(STNode expression, boolean isRhsExpr, STNode rightArrow) {
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:
STNode defaultKeyword = parseDefaultKeyword();
name = STNodeFactory.createSimpleNameReferenceNode(defaultKeyword);
return parseAsyncSendAction(expression, rightArrow, name);
case IDENTIFIER_TOKEN:
name = STNodeFactory.createSimpleNameReferenceNode(parseFunctionName());
break;
case CONTINUE_KEYWORD:
case COMMIT_KEYWORD:
name = getKeywordAsSimpleNameRef();
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 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> keyword token.
*
* @return Parsed node
*/
private STNode parseParameterizedTypeKeyword() {
STToken nextToken = peek();
switch (nextToken.kind) {
case MAP_KEYWORD:
case FUTURE_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 = parseQualifiedIdentifier(ParserRuleContext.ANNOT_DECL_OPTIONAL_TYPE);
if (typeDescOrAnnotTag.kind == SyntaxKind.QUALIFIED_NAME_REFERENCE) {
STNode annotTag = parseAnnotationTag();
return parseAnnotationDeclAttachPoints(metadata, qualifier, constKeyword, annotationKeyword,
typeDescOrAnnotTag, annotTag);
}
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.IDENTIFIER_TOKEN || isValidTypeContinuationToken(nextToken)) {
STNode typeDesc = parseComplexTypeDescriptor(typeDescOrAnnotTag,
ParserRuleContext.TYPE_DESC_IN_ANNOTATION_DECL, false);
STNode annotTag = parseAnnotationTag();
return parseAnnotationDeclAttachPoints(metadata, qualifier, constKeyword, annotationKeyword, typeDesc,
annotTag);
}
STNode annotTag = ((STSimpleNameReferenceNode) typeDescOrAnnotTag).name;
return parseAnnotationDeclRhs(metadata, qualifier, constKeyword, annotationKeyword, annotTag);
}
/**
* 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.createEmptyNodeList();
break;
case ON_KEYWORD:
onKeyword = parseOnKeyword();
attachPoints = parseAnnotationAttachPoints();
onKeyword = cloneWithDiagnosticIfListEmpty(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 = SyntaxErrors.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 parseXMLNamespaceDeclaration(boolean isModuleVar) {
startContext(ParserRuleContext.XML_NAMESPACE_DECLARATION);
STNode xmlnsKeyword = parseXMLNSKeyword();
STNode namespaceUri = parseSimpleConstExpr();
while (!isValidXMLNameSpaceURI(namespaceUri)) {
xmlnsKeyword = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(xmlnsKeyword,
namespaceUri, DiagnosticErrorCode.ERROR_INVALID_XML_NAMESPACE_URI);
namespaceUri = parseSimpleConstExpr();
}
STNode xmlnsDecl = parseXMLDeclRhs(xmlnsKeyword, namespaceUri, isModuleVar);
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;
}
}
private boolean isValidXMLNameSpaceURI(STNode expr) {
switch (expr.kind) {
case STRING_LITERAL:
case QUALIFIED_NAME_REFERENCE:
case SIMPLE_NAME_REFERENCE:
return true;
case IDENTIFIER_TOKEN:
default:
return false;
}
}
private STNode parseSimpleConstExpr() {
startContext(ParserRuleContext.CONSTANT_EXPRESSION);
STNode expr = parseSimpleConstExprInternal();
endContext();
return expr;
}
private STNode parseSimpleConstExprInternal() {
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_PAREN_TOKEN:
return parseNilLiteral();
default:
STToken token = peek();
Solution solution = recover(token, ParserRuleContext.CONSTANT_EXPRESSION_START);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return STNodeFactory.createSimpleNameReferenceNode(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, boolean isModuleVar) {
return parseXMLDeclRhs(peek().kind, xmlnsKeyword, namespaceUri, isModuleVar);
}
private STNode parseXMLDeclRhs(SyntaxKind nextTokenKind, STNode xmlnsKeyword, STNode namespaceUri,
boolean isModuleVar) {
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, isModuleVar);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseXMLDeclRhs(solution.tokenKind, xmlnsKeyword, namespaceUri, isModuleVar);
}
STNode semicolon = parseSemicolon();
if (isModuleVar) {
return STNodeFactory.createModuleXMLNamespaceDeclarationNode(xmlnsKeyword, namespaceUri, asKeyword,
namespacePrefix, semicolon);
}
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 = parseOptionalAnnotations();
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 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,
boolean isTypedBindingPattern) {
STNode pipeToken = parsePipeToken();
STNode rightTypeDesc = parseTypeDescriptor(context, isTypedBindingPattern, false);
return createUnionTypeDesc(leftTypeDesc, pipeToken, rightTypeDesc);
}
private STNode createUnionTypeDesc(STNode leftTypeDesc, STNode pipeToken, STNode rightTypeDesc) {
leftTypeDesc = validateForUsageOfVar(leftTypeDesc);
rightTypeDesc = validateForUsageOfVar(rightTypeDesc);
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:
case DISTINCT_KEYWORD:
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 TYPEDESC_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 READONLY_KEYWORD:
return SyntaxKind.READONLY_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 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();
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:
if (workers.isEmpty()) {
openBrace = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(openBrace, stmt,
DiagnosticErrorCode.ERROR_ONLY_NAMED_WORKERS_ALLOWED_HERE);
} else {
updateLastNodeInListWithInvalidNode(workers, stmt,
DiagnosticErrorCode.ERROR_ONLY_NAMED_WORKERS_ALLOWED_HERE);
}
}
}
STNode namedWorkerDeclarations = STNodeFactory.createNodeList(workers);
STNode closeBrace = parseCloseBrace();
endContext();
STNode forkStmt =
STNodeFactory.createForkStatementNode(forkKeyword, openBrace, namedWorkerDeclarations, closeBrace);
if (isNodeListEmpty(namedWorkerDeclarations)) {
return SyntaxErrors.addDiagnostic(forkStmt,
DiagnosticErrorCode.ERROR_MISSING_NAMED_WORKER_DECLARATION_IN_FORK_STMT);
}
return forkStmt;
}
/**
* 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, boolean isInConditionalExpr) {
STNode trapKeyword = parseTrapKeyword();
STNode expr =
parseExpression(OperatorPrecedence.EXPRESSION_ACTION, isRhsExpr, allowActions, isInConditionalExpr);
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.createEmptyNodeList();
}
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, boolean allowActions, boolean isInConditionalExpr) {
startContext(ParserRuleContext.TYPE_CAST);
STNode ltToken = parseLTToken();
STNode typeCastParam = parseTypeCastParam();
STNode gtToken = parseGTToken();
endContext();
STNode expression =
parseExpression(OperatorPrecedence.EXPRESSION_ACTION, isRhsExpr, allowActions, isInConditionalExpr);
return STNodeFactory.createTypeCastExpressionNode(ltToken, typeCastParam, gtToken, expression);
}
private STNode parseTypeCastParam() {
STNode annot;
STNode type;
STToken token = peek();
switch (token.kind) {
case AT_TOKEN:
annot = parseOptionalAnnotations();
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() {
STToken nextToken = peek();
if (isEndOfTableRowList(nextToken.kind)) {
return STNodeFactory.createEmptyNodeList();
}
List<STNode> mappings = new ArrayList<>();
STNode mapExpr = parseMappingConstructorExpr();
mappings.add(mapExpr);
nextToken = peek();
STNode rowEnd;
while (!isEndOfTableRowList(nextToken.kind)) {
rowEnd = parseTableRowEnd(nextToken.kind);
if (rowEnd == null) {
break;
}
mappings.add(rowEnd);
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);
}
}
private STNode parseTableRowEnd() {
STNode nextToken = peek();
return parseTableRowEnd(nextToken.kind);
}
private STNode parseTableRowEnd(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_BRACKET_TOKEN:
case EOF_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.TABLE_ROW_END);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseListConstructorMemberEnd(solution.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 (isKeyKeyword(token)) {
return getKeyKeyword(consume());
} else {
Solution sol = recover(token, ParserRuleContext.KEY_KEYWORD);
return sol.recoveredNode;
}
}
static boolean isKeyKeyword(STToken token) {
return token.kind == SyntaxKind.IDENTIFIER_TOKEN && LexerTerminals.KEY.equals(token.text());
}
private STNode getKeyKeyword(STToken token) {
return STNodeFactory.createToken(SyntaxKind.KEY_KEYWORD, token.leadingMinutiae(), token.trailingMinutiae(),
token.diagnostics());
}
/**
* Parse field names.
* <p>
* <code>field-name-list := [ field-name (, field-name)* ]</code>
*
* @return Parsed node
*/
private STNode parseFieldNames() {
STToken nextToken = peek();
if (isEndOfFieldNamesList(nextToken.kind)) {
return STNodeFactory.createEmptyNodeList();
}
List<STNode> fieldNames = 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();
if (nextToken.kind == SyntaxKind.LT_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 typedesc type descriptor.
* typedesc-type-descriptor := typedesc type-parameter
*
* @return Parsed typedesc type node
*/
private STNode parseTypedescTypeDescriptor() {
STNode typedescKeywordToken = parseTypedescKeyword();
STNode typedescTypeParamsNode;
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.LT_TOKEN) {
typedescTypeParamsNode = parseTypeParameter();
} else {
typedescTypeParamsNode = STNodeFactory.createEmptyNode();
}
return STNodeFactory.createTypedescTypeDescriptorNode(typedescKeywordToken, typedescTypeParamsNode);
}
/**
* Parse typedesc-keyword.
*
* @return Parsed typedesc-keyword node
*/
private STNode parseTypedescKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.TYPEDESC_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.TYPEDESC_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 xml type descriptor.
* xml-type-descriptor := xml type-parameter
*
* @return Parsed typedesc type node
*/
private STNode parseXmlTypeDescriptor() {
STNode xmlKeywordToken = parseXMLKeyword();
STNode typedescTypeParamsNode;
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.LT_TOKEN) {
typedescTypeParamsNode = parseTypeParameter();
} else {
typedescTypeParamsNode = STNodeFactory.createEmptyNode();
}
return STNodeFactory.createXmlTypeDescriptorNode(xmlKeywordToken, typedescTypeParamsNode);
}
/**
* 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, false);
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, false);
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 = cloneWithDiagnosticIfListEmpty(letVarDeclarations, letKeyword,
DiagnosticErrorCode.ERROR_MISSING_LET_VARIABLE_DECLARATION);
STNode expression = parseExpression(OperatorPrecedence.QUERY, 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 = parseOptionalAnnotations();
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();
while (true) {
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.EOF_TOKEN || nextToken.kind == SyntaxKind.CLOSE_BRACE_TOKEN) {
break;
} else {
nextToken = consume();
expr = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(expr, nextToken,
DiagnosticErrorCode.ERROR_INVALID_TOKEN, nextToken.text());
}
}
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;
}
}
/**
* 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 (isKeyKeyword(nextToken)) {
STNode keyKeywordToken = getKeyKeyword(consume());
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.
* @param isRhsExpr Is expression in rhs context
* @return Anonymous function expression node
*/
private STNode parseExplicitFunctionExpression(STNode annots, boolean isRhsExpr) {
startContext(ParserRuleContext.ANON_FUNC_EXPRESSION);
STNode funcKeyword = parseFunctionKeyword();
STNode funcSignature = parseFuncSignature(false);
STNode funcBody = parseAnonFuncBody(isRhsExpr);
return STNodeFactory.createExplicitAnonymousFunctionExpressionNode(annots, funcKeyword, funcSignature,
funcBody);
}
/**
* Parse anonymous function body.
* <p>
* <code>anon-func-body := block-function-body | expr-function-body</code>
*
* @param isRhsExpr Is expression in rhs context
* @return Anon function body node
*/
private STNode parseAnonFuncBody(boolean isRhsExpr) {
return parseAnonFuncBody(peek().kind, isRhsExpr);
}
private STNode parseAnonFuncBody(SyntaxKind nextTokenKind, boolean isRhsExpr) {
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, isRhsExpr);
default:
Solution solution = recover(peek(), ParserRuleContext.ANON_FUNC_BODY, isRhsExpr);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseAnonFuncBody(solution.tokenKind, isRhsExpr);
}
}
/**
* Parse expression function body.
* <p>
* <code>expr-function-body := => expression</code>
*
* @param isAnon Is anonymous function.
* @param isRhsExpr Is expression in rhs context
* @return Expression function body node
*/
private STNode parseExpressionFuncBody(boolean isAnon, boolean isRhsExpr) {
STNode rightDoubleArrow = parseDoubleRightArrow();
STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
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, boolean isRhsExpr) {
switch (params.kind) {
case SIMPLE_NAME_REFERENCE:
case INFER_PARAM_LIST:
break;
case BRACED_EXPRESSION:
params = getAnonFuncParam((STBracedExpressionNode) params);
break;
default:
STToken syntheticParam = STNodeFactory.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN);
syntheticParam = SyntaxErrors.cloneWithLeadingInvalidNodeMinutiae(syntheticParam, params,
DiagnosticErrorCode.ERROR_INVALID_PARAM_LIST_IN_INFER_ANONYMOUS_FUNCTION_EXPR);
params = STNodeFactory.createSimpleNameReferenceNode(syntheticParam);
}
STNode rightDoubleArrow = parseDoubleRightArrow();
STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
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
* @param isRhsExpr Is expression in rhs context
* @return Implicit anon function expression node
*/
private STNode parseImplicitAnonFunc(STNode openParen, STNode firstParam, boolean isRhsExpr) {
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);
param = STNodeFactory.createSimpleNameReferenceNode(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, isRhsExpr);
}
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 = cloneWithDiagnosticIfListEmpty(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, false);
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, false);
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 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
*               [query-construct-type] query-pipeline select-clause on-conflict-clause? limit-lause?
* <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 = parseQueryConstructType(parseStreamKeyword(), null);
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) {
STToken nextToken = peek();
return parseTableConstructorOrQuery(nextToken.kind, nextToken, tableKeyword, isRhsExpr);
}
private STNode parseTableConstructorOrQuery(SyntaxKind nextTokenKind, STToken nextToken, 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);
case IDENTIFIER_TOKEN:
if (isKeyKeyword(nextToken)) {
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, null, 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 | stream</code>
*
* @return Parsed node
*/
private STNode parseQueryConstructType(STNode keyword, STNode keySpecifier) {
return STNodeFactory.createQueryConstructTypeNode(keyword, keySpecifier);
}
/**
* Parse query expression.
* <p>
* <code>
* query-expr-rhs := query-pipeline select-clause
*                   query-pipeline select-clause on-conflict-clause? limit-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, SyntaxKind.NONE)) {
intermediateClause = parseIntermediateClause(isRhsExpr);
if (intermediateClause == null) {
break;
}
if (selectClause != null) {
selectClause = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(selectClause, intermediateClause,
DiagnosticErrorCode.ERROR_MORE_CLAUSES_AFTER_SELECT_CLAUSE);
continue;
}
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, isRhsExpr);
}
if (selectClause == null) {
STNode selectKeyword = SyntaxErrors.createMissingToken(SyntaxKind.SELECT_KEYWORD);
STNode expr = STNodeFactory
.createSimpleNameReferenceNode(SyntaxErrors.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN));
selectClause = STNodeFactory.createSelectClauseNode(selectKeyword, expr);
if (clauses.isEmpty()) {
fromClause = SyntaxErrors.addDiagnostic(fromClause, DiagnosticErrorCode.ERROR_MISSING_SELECT_CLAUSE);
} else {
int lastIndex = clauses.size() - 1;
STNode intClauseWithDiagnostic = SyntaxErrors.addDiagnostic(clauses.get(lastIndex),
DiagnosticErrorCode.ERROR_MISSING_SELECT_CLAUSE);
clauses.set(lastIndex, intClauseWithDiagnostic);
}
}
STNode intermediateClauses = STNodeFactory.createNodeList(clauses);
STNode queryPipeline = STNodeFactory.createQueryPipelineNode(fromClause, intermediateClauses);
STNode onConflictClause = parseOnConflictClause(isRhsExpr);
STNode limitClause = parseLimitClause(isRhsExpr);
return STNodeFactory.createQueryExpressionNode(queryConstructType, queryPipeline, selectClause,
onConflictClause, limitClause);
}
/**
* Parse limit keyword.
*
* @return Limit keyword node
*/
private STNode parseLimitKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.LIMIT_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.LIMIT_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse an intermediate clause.
* <p>
* <code>
* intermediate-clause := from-clause | where-clause | let-clause
* </code>
*
* @return Parsed node
*/
private STNode parseIntermediateClause(boolean isRhsExpr) {
STToken nextToken = peek();
return parseIntermediateClause(nextToken.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 JOIN_KEYWORD:
case OUTER_KEYWORD:
return parseJoinClause(isRhsExpr);
case ORDER_KEYWORD:
case BY_KEYWORD:
case ASCENDING_KEYWORD:
case DESCENDING_KEYWORD:
return parseOrderByClause(isRhsExpr);
case DO_KEYWORD:
case SEMICOLON_TOKEN:
case ON_KEYWORD:
case CONFLICT_KEYWORD:
case LIMIT_KEYWORD:
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);
}
}
/**
* Parse select-keyword.
*
* @return Select-keyword node
*/
private STNode parseJoinKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.JOIN_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.JOIN_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse select-keyword.
*
* @return Select-keyword node
*/
private STNode parseOuterKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.OUTER_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.OUTER_KEYWORD);
return sol.recoveredNode;
}
}
private boolean isEndOfIntermediateClause(SyntaxKind tokenKind, SyntaxKind precedingNodeKind) {
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_STRING:
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, precedingNodeKind);
}
}
/**
* 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 = cloneWithDiagnosticIfListEmpty(letVarDeclarations, letKeyword,
DiagnosticErrorCode.ERROR_MISSING_LET_VARIABLE_DECLARATION);
return STNodeFactory.createLetClauseNode(letKeyword, letVarDeclarations);
}
/**
* Parse order-keyword.
*
* @return Order-keyword node
*/
private STNode parseOrderKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.ORDER_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.ORDER_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse by-keyword.
*
* @return By-keyword node
*/
private STNode parseByKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.BY_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.BY_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse ascending-keyword.
*
* @return Ascending-keyword node
*/
private STNode parseAscendingKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.ASCENDING_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.ASCENDING_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse descending-keyword.
*
* @return Descending-keyword node
*/
private STNode parseDescendingKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.DESCENDING_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.DESCENDING_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse order by clause.
* <p>
* <code>order-by-clause := order by order-key-list
* </code>
*
* @return Parsed node
*/
private STNode parseOrderByClause(boolean isRhsExpr) {
STNode orderKeyword = parseOrderKeyword();
STNode byKeyword = parseByKeyword();
STNode orderKeys = parseOrderKeyList(isRhsExpr);
byKeyword = cloneWithDiagnosticIfListEmpty(orderKeys, byKeyword,
DiagnosticErrorCode.ERROR_MISSING_ORDER_KEY);
return STNodeFactory.createOrderByClauseNode(orderKeyword, byKeyword, orderKeys);
}
/**
* Parse order key.
* <p>
* <code>order-key-list := order-key [, order-key]*</code>
*
* @return Parsed node
*/
private STNode parseOrderKeyList(boolean isRhsExpr) {
startContext(ParserRuleContext.ORDER_KEY);
List<STNode> orderKeys = new ArrayList<>();
STToken nextToken = peek();
if (isEndOfOrderKeys(nextToken.kind)) {
endContext();
return STNodeFactory.createEmptyNodeList();
}
STNode orderKey = parseOrderKey(isRhsExpr);
orderKeys.add(orderKey);
nextToken = peek();
STNode orderKeyListMemberEnd;
while (!isEndOfOrderKeys(nextToken.kind)) {
orderKeyListMemberEnd = parseOrderKeyListMemberEnd();
if (orderKeyListMemberEnd == null) {
break;
}
orderKeys.add(orderKeyListMemberEnd);
orderKey = parseOrderKey(isRhsExpr);
orderKeys.add(orderKey);
nextToken = peek();
}
endContext();
return STNodeFactory.createNodeList(orderKeys);
}
private boolean isEndOfOrderKeys(SyntaxKind tokenKind) {
switch (tokenKind) {
case COMMA_TOKEN:
return false;
case SEMICOLON_TOKEN:
case EOF_TOKEN:
return true;
default:
return isNextQueryClauseStart(tokenKind);
}
}
private boolean isNextQueryClauseStart(SyntaxKind tokenKind) {
switch(tokenKind) {
case SELECT_KEYWORD:
case LET_KEYWORD:
case WHERE_KEYWORD:
return true;
default:
return false;
}
}
private STNode parseOrderKeyListMemberEnd() {
return parseOrderKeyListMemberEnd(peek().kind);
}
private STNode parseOrderKeyListMemberEnd(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case SELECT_KEYWORD:
case WHERE_KEYWORD:
case LET_KEYWORD:
case EOF_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.ORDER_KEY_LIST_END);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseOrderKeyListMemberEnd(solution.tokenKind);
}
}
/**
* Parse order key.
* <p>
* <code>order-key := expression (ascending | descending)?</code>
*
* @return Parsed node
*/
private STNode parseOrderKey(boolean isRhsExpr) {
STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
STToken nextToken = peek();
switch (nextToken.kind) {
case ASCENDING_KEYWORD:
STNode ascendingKeyword = parseAscendingKeyword();
return STNodeFactory.createOrderKeyNode(expression, ascendingKeyword);
case DESCENDING_KEYWORD:
STNode descendingKeyword = parseDescendingKeyword();
return STNodeFactory.createOrderKeyNode(expression, descendingKeyword);
default:
return STNodeFactory.createOrderKeyNode(expression, STNodeFactory.createEmptyNode());
}
}
/**
* 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 on-conflict clause.
* <p>
* <code>
* onConflictClause := on conflict expression
* </code>
*
* @return On conflict clause node
*/
private STNode parseOnConflictClause(boolean isRhsExpr) {
STToken nextToken = peek();
if (nextToken.kind != SyntaxKind.ON_KEYWORD) {
return STNodeFactory.createEmptyNode();
}
STNode onKeyword = parseOnKeyword();
STNode conflictKeyword = parseConflictKeyword();
STNode expr = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
return STNodeFactory.createOnConflictClauseNode(onKeyword, conflictKeyword, expr);
}
/**
* Parse conflict keyword.
*
* @return Conflict keyword node
*/
private STNode parseConflictKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.CONFLICT_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.CONFLICT_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse limit clause.
* <p>
* <code>limitClause := limit expression</code>
*
* @return Limit expression node
*/
private STNode parseLimitClause(boolean isRhsExpr) {
STToken nextToken = peek();
if (nextToken.kind != SyntaxKind.LIMIT_KEYWORD) {
return STNodeFactory.createEmptyNode();
}
STNode limitKeyword = parseLimitKeyword();
STNode expr = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
return STNodeFactory.createLimitClauseNode(limitKeyword, expr);
}
/**
* Parse join clause.
* <p>
* <code>
* join-clause := (join-var-decl | outer-join-var-decl) in expression
* <br/>
* join-var-decl := join (typeName | var) bindingPattern
* <br/>
* outer-join-var-decl := outer join var binding-pattern
* </code>
*
* @return Join clause
*/
private STNode parseJoinClause(boolean isRhsExpr) {
STNode outerKeyword;
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.OUTER_KEYWORD) {
outerKeyword = parseOuterKeyword();
} else {
outerKeyword = STNodeFactory.createEmptyNode();
}
STNode joinKeyword = parseJoinKeyword();
STNode typedBindingPattern = parseTypedBindingPattern(ParserRuleContext.JOIN_CLAUSE);
STNode inKeyword = parseInKeyword();
STNode onCondition;
STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
nextToken = peek();
if (nextToken.kind == SyntaxKind.ON_KEYWORD) {
onCondition = parseOnClause(isRhsExpr);
} else {
onCondition = STNodeFactory.createEmptyNode();
}
return STNodeFactory.createJoinClauseNode(outerKeyword, joinKeyword, typedBindingPattern, inKeyword, expression,
onCondition);
}
/**
* Parse on clause.
* <p>
* <code>on clause := on expression</code>
*
* @return On clause node
*/
private STNode parseOnClause(boolean isRhsExpr) {
STNode onKeyword = parseOnKeyword();
STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
return STNodeFactory.createOnClauseNode(onKeyword, expression);
}
/**
* 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();
switch (expr.kind) {
case FUNCTION_CALL:
case METHOD_CALL:
case REMOTE_METHOD_CALL_ACTION:
break;
case SIMPLE_NAME_REFERENCE:
case QUALIFIED_NAME_REFERENCE:
STNode openParenToken = SyntaxErrors.createMissingTokenWithDiagnostics(SyntaxKind.OPEN_PAREN_TOKEN,
DiagnosticErrorCode.ERROR_MISSING_OPEN_PAREN_TOKEN);
STNode arguments = STNodeFactory.createEmptyNodeList();
STNode closeParenToken = SyntaxErrors.createMissingTokenWithDiagnostics(SyntaxKind.CLOSE_PAREN_TOKEN,
DiagnosticErrorCode.ERROR_MISSING_CLOSE_PAREN_TOKEN);
expr = STNodeFactory.createFunctionCallExpressionNode(expr, openParenToken, arguments, closeParenToken);
break;
default:
startKeyword = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(startKeyword, expr,
DiagnosticErrorCode.ERROR_INVALID_EXPRESSION_IN_START_ACTION);
STNode funcName = SyntaxErrors.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN);
funcName = STNodeFactory.createSimpleNameReferenceNode(funcName);
openParenToken = SyntaxErrors.createMissingToken(SyntaxKind.OPEN_PAREN_TOKEN);
arguments = STNodeFactory.createEmptyNodeList();
closeParenToken = SyntaxErrors.createMissingToken(SyntaxKind.CLOSE_PAREN_TOKEN);
expr = STNodeFactory.createFunctionCallExpressionNode(funcName, openParenToken, arguments,
closeParenToken);
break;
}
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;
}
}
/**
* 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,
boolean isTypedBindingPattern) {
STNode bitwiseAndToken = consume();
STNode rightTypeDesc = parseTypeDescriptor(context, isTypedBindingPattern, false);
return createIntersectionTypeDesc(leftTypeDesc, bitwiseAndToken, rightTypeDesc);
}
private STNode createIntersectionTypeDesc(STNode leftTypeDesc, STNode bitwiseAndToken, STNode rightTypeDesc) {
leftTypeDesc = validateForUsageOfVar(leftTypeDesc);
rightTypeDesc = validateForUsageOfVar(rightTypeDesc);
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 = parseSimpleConstExpr();
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 = parseBasicLiteral();
break;
default:
literal = parseDecimalIntLiteral(ParserRuleContext.DECIMAL_INTEGER_LITERAL);
literal = STNodeFactory.createBasicLiteralNode(literal.kind, 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) {
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:
SyntaxKind nextNextTokenKind = peek(nextTokenIndex).kind;
return nextNextTokenKind == SyntaxKind.SEMICOLON_TOKEN || nextNextTokenKind == SyntaxKind.COMMA_TOKEN ||
nextNextTokenKind == SyntaxKind.CLOSE_BRACKET_TOKEN ||
isValidExprRhsStart(nextNextTokenKind, SyntaxKind.SIMPLE_NAME_REFERENCE);
case IDENTIFIER_TOKEN:
return isValidExprRhsStart(peek(nextTokenIndex).kind, SyntaxKind.SIMPLE_NAME_REFERENCE);
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 FROM_KEYWORD:
case LET_KEYWORD:
case BACKTICK_TOKEN:
case NEW_KEYWORD:
case FAIL_KEYWORD:
case LEFT_ARROW_TOKEN:
return true;
case PLUS_TOKEN:
case MINUS_TOKEN:
return isValidExpressionStart(peek(nextTokenIndex).kind, nextTokenIndex);
case FUNCTION_KEYWORD:
case TABLE_KEYWORD:
return peek(nextTokenIndex).kind == SyntaxKind.FROM_KEYWORD;
case STREAM_KEYWORD:
STToken nextNextToken = peek(nextTokenIndex);
return nextNextToken.kind == SyntaxKind.KEY_KEYWORD ||
nextNextToken.kind == SyntaxKind.OPEN_BRACKET_TOKEN ||
nextNextToken.kind == SyntaxKind.FROM_KEYWORD;
case ERROR_KEYWORD:
return peek(nextTokenIndex).kind == SyntaxKind.OPEN_PAREN_TOKEN;
case SERVICE_KEYWORD:
return peek(nextTokenIndex).kind == SyntaxKind.OPEN_BRACE_TOKEN;
case XML_KEYWORD:
case STRING_KEYWORD:
return peek(nextTokenIndex).kind == SyntaxKind.BACKTICK_TOKEN;
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);
if (sol.action == Action.REMOVE) {
return sol.recoveredNode;
}
return STNodeFactory.createSimpleNameReferenceNode(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();
openBrace = cloneWithDiagnosticIfListEmpty(receiveFields, openBrace,
DiagnosticErrorCode.ERROR_MISSING_RECEIVE_FIELD_IN_RECEIVE_ACTION);
return STNodeFactory.createReceiveFieldsNode(openBrace, receiveFields, closeBrace);
}
private STNode parseReceiveFields() {
List<STNode> receiveFields = new ArrayList<>();
STToken nextToken = peek();
if (isEndOfReceiveFields(nextToken.kind)) {
return STNodeFactory.createEmptyNodeList();
}
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_BRACE_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:
STNode defaultKeyword = parseDefaultKeyword();
return STNodeFactory.createSimpleNameReferenceNode(defaultKeyword);
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 = consume();
STToken endLGToken = consume();
STNode doubleGTToken = STNodeFactory.createToken(SyntaxKind.DOUBLE_GT_TOKEN, openGTToken.leadingMinutiae(),
endLGToken.trailingMinutiae());
if (!validateRightShiftOperatorWS(openGTToken)) {
doubleGTToken = SyntaxErrors.addDiagnostic(doubleGTToken,
DiagnosticErrorCode.ERROR_NO_WHITESPACES_ALLOWED_IN_RIGHT_SHIFT_OP);
}
return doubleGTToken;
}
/**
* Parse unsigned right shift token (>>>).
*
* @return Parsed node
*/
private STNode parseUnsignedRightShiftToken() {
STNode openGTToken = consume();
STNode middleGTToken = consume();
STNode endLGToken = consume();
STNode unsignedRightShiftToken = STNodeFactory.createToken(SyntaxKind.TRIPPLE_GT_TOKEN,
openGTToken.leadingMinutiae(), endLGToken.trailingMinutiae());
boolean validOpenGTToken = validateRightShiftOperatorWS(openGTToken);
boolean validMiddleGTToken = validateRightShiftOperatorWS(middleGTToken);
if (validOpenGTToken && validMiddleGTToken) {
return unsignedRightShiftToken;
}
unsignedRightShiftToken = SyntaxErrors.addDiagnostic(unsignedRightShiftToken,
DiagnosticErrorCode.ERROR_NO_WHITESPACES_ALLOWED_IN_UNSIGNED_RIGHT_SHIFT_OP);
return unsignedRightShiftToken;
}
/**
* Validate the whitespace between '>' tokens of right shift operators.
*
* @param node Preceding node
* @return the validated node
*/
private boolean validateRightShiftOperatorWS(STNode node) {
int diff = node.widthWithTrailingMinutiae() - node.width();
return diff == 0;
}
/**
* 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);
STToken nextToken = peek();
if (isEndOfWaitFutureExprList(nextToken.kind)) {
endContext();
STNode waitFutureExprs = STNodeFactory
.createSimpleNameReferenceNode(STNodeFactory.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN));
waitFutureExprs = SyntaxErrors.addDiagnostic(waitFutureExprs,
DiagnosticErrorCode.ERROR_MISSING_WAIT_FUTURE_EXPRESSION);
return STNodeFactory.createWaitActionNode(waitKeyword, waitFutureExprs);
}
List<STNode> waitFutureExprList = new ArrayList<>();
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 = parseActionOrExpression();
if (waitFutureExpr.kind == SyntaxKind.MAPPING_CONSTRUCTOR) {
waitFutureExpr = SyntaxErrors.addDiagnostic(waitFutureExpr,
DiagnosticErrorCode.ERROR_MAPPING_CONSTRUCTOR_EXPR_AS_A_WAIT_EXPR);
} else if (isAction(waitFutureExpr)) {
waitFutureExpr =
SyntaxErrors.addDiagnostic(waitFutureExpr, DiagnosticErrorCode.ERROR_ACTION_AS_A_WAIT_EXPR);
}
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();
openBrace = cloneWithDiagnosticIfListEmpty(waitFields, openBrace,
DiagnosticErrorCode.ERROR_MISSING_WAIT_FIELD_IN_WAIT_ACTION);
STNode waitFieldsNode = STNodeFactory.createWaitFieldsListNode(openBrace, waitFields, closeBrace);
return STNodeFactory.createWaitActionNode(waitKeyword, waitFieldsNode);
}
private STNode parseWaitFields() {
List<STNode> waitFields = new ArrayList<>();
STToken nextToken = peek();
if (isEndOfWaitFields(nextToken.kind)) {
return STNodeFactory.createEmptyNodeList();
}
STNode waitField = parseWaitField();
waitFields.add(waitField);
nextToken = peek();
STNode waitFieldEnd;
while (!isEndOfWaitFields(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 boolean isEndOfWaitFields(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case EOF_TOKEN:
case CLOSE_BRACE_TOKEN:
return true;
default:
return false;
}
}
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);
identifier = STNodeFactory.createSimpleNameReferenceNode(identifier);
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, boolean isInConditionalExpr) {
STNode annotAccessToken = parseAnnotChainingToken();
STNode annotTagReference = parseFieldAccessIdentifier(isInConditionalExpr);
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(boolean isInConditionalExpr) {
return parseQualifiedIdentifier(ParserRuleContext.FIELD_ACCESS_IDENTIFIER, isInConditionalExpr);
}
/**
* 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, boolean isRhsExpr) {
if (selectClause != null) {
queryPipeline = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(queryPipeline, selectClause,
DiagnosticErrorCode.ERROR_SELECT_CLAUSE_IN_QUERY_ACTION);
}
startContext(ParserRuleContext.DO_CLAUSE);
STNode doKeyword = parseDoKeyword();
STNode blockStmt = parseBlockNode();
endContext();
STNode limitClause = parseLimitClause(isRhsExpr);
return STNodeFactory.createQueryActionNode(queryPipeline, doKeyword, blockStmt, limitClause);
}
/**
* 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, boolean isInConditionalExpr) {
STNode optionalFieldAccessToken = parseOptionalChainingToken();
STNode fieldName = parseFieldAccessIdentifier(isInConditionalExpr);
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.ANON_FUNC_OR_LET, true, false, true);
STNode nextToken = peek();
STNode endExpr;
STNode colon;
if (nextToken.kind != SyntaxKind.COLON_TOKEN && middleExpr.kind == SyntaxKind.QUALIFIED_NAME_REFERENCE) {
STQualifiedNameReferenceNode qualifiedNameRef = (STQualifiedNameReferenceNode) middleExpr;
middleExpr = STNodeFactory.createSimpleNameReferenceNode(qualifiedNameRef.modulePrefix);
colon = qualifiedNameRef.colon;
endContext();
endExpr = STNodeFactory.createSimpleNameReferenceNode(qualifiedNameRef.identifier);
} else {
colon = parseColon();
endContext();
endExpr = parseExpression(OperatorPrecedence.ANON_FUNC_OR_LET, 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();
openBraceToken = cloneWithDiagnosticIfListEmpty(enumMemberList, openBraceToken,
DiagnosticErrorCode.ERROR_MISSING_ENUM_MEMBER);
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);
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.CLOSE_BRACE_TOKEN) {
return STNodeFactory.createEmptyNodeList();
}
List<STNode> enumMemberList = 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_STRING:
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);
if (startingBackTick.isMissing()) {
startingBackTick = SyntaxErrors.createMissingToken(SyntaxKind.BACKTICK_TOKEN);
STNode endingBackTick = SyntaxErrors.createMissingToken(SyntaxKind.BACKTICK_TOKEN);
STNode content = STNodeFactory.createEmptyNode();
STNode byteArrayLiteral =
STNodeFactory.createByteArrayLiteralNode(type, startingBackTick, content, endingBackTick);
byteArrayLiteral =
SyntaxErrors.addDiagnostic(byteArrayLiteral, DiagnosticErrorCode.ERROR_MISSING_BYTE_ARRAY_CONTENT);
return byteArrayLiteral;
}
STNode content = parseByteArrayContent(kind);
return parseByteArrayLiteral(kind, type, startingBackTick, content);
}
/**
* Parse byte array literal.
*
* @param baseKind indicates the SyntaxKind base16 or base64
* @param typeKeyword keyword token, possible values are `base16` and `base64`
* @param startingBackTick starting backtick token
* @param byteArrayContent byte array literal content to be validated
* @return parsed byte array literal node
*/
private STNode parseByteArrayLiteral(SyntaxKind baseKind, STNode typeKeyword, STNode startingBackTick,
STNode byteArrayContent) {
STNode content = STNodeFactory.createEmptyNode();
STNode newStartingBackTick = startingBackTick;
STNodeList items = (STNodeList) byteArrayContent;
if (items.size() == 1) {
STNode item = items.get(0);
if (baseKind == SyntaxKind.BASE16_KEYWORD && !isValidBase16LiteralContent(item.toString())) {
newStartingBackTick = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(startingBackTick, item,
DiagnosticErrorCode.ERROR_INVALID_BASE16_CONTENT_IN_BYTE_ARRAY_LITERAL);
} else if (baseKind == SyntaxKind.BASE64_KEYWORD && !isValidBase64LiteralContent(item.toString())) {
newStartingBackTick = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(startingBackTick, item,
DiagnosticErrorCode.ERROR_INVALID_BASE64_CONTENT_IN_BYTE_ARRAY_LITERAL);
} else if (item.kind != SyntaxKind.TEMPLATE_STRING) {
newStartingBackTick = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(startingBackTick, item,
DiagnosticErrorCode.ERROR_INVALID_CONTENT_IN_BYTE_ARRAY_LITERAL);
} else {
content = item;
}
} else if (items.size() > 1) {
STNode clonedStartingBackTick = startingBackTick;
for (int index = 0; index < items.size(); index++) {
STNode item = items.get(index);
clonedStartingBackTick =
SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(clonedStartingBackTick, item);
}
newStartingBackTick = SyntaxErrors.addDiagnostic(clonedStartingBackTick,
DiagnosticErrorCode.ERROR_INVALID_CONTENT_IN_BYTE_ARRAY_LITERAL);
}
STNode endingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_END);
return STNodeFactory.createByteArrayLiteralNode(typeKeyword, newStartingBackTick, 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) {
STToken nextToken = peek();
List<STNode> items = new ArrayList<>();
while (!isEndOfBacktickContent(nextToken.kind)) {
STNode content = parseTemplateItem();
items.add(content);
nextToken = peek();
}
return STNodeFactory.createNodeList(items);
}
/**
* Validate base16 literal content.
* <p>
* <code>
* Base16Literal := base16 WS ` HexGroup* WS `
* <br/>
* HexGroup := WS HexDigit WS HexDigit
* <br/>
* WS := WhiteSpaceChar*
* <br/>
* WhiteSpaceChar := 0x9 | 0xA | 0xD | 0x20
* </code>
*
* @param content the string surrounded by the backticks
* @return <code>true</code>, if the string content is valid. <code>false</code> otherwise.
*/
static boolean isValidBase16LiteralContent(String content) {
char[] charArray = content.toCharArray();
int hexDigitCount = 0;
for (char c : charArray) {
switch (c) {
case LexerTerminals.TAB:
case LexerTerminals.NEWLINE:
case LexerTerminals.CARRIAGE_RETURN:
case LexerTerminals.SPACE:
break;
default:
if (isHexDigit(c)) {
hexDigitCount++;
} else {
return false;
}
break;
}
}
return hexDigitCount % 2 == 0;
}
/**
* Validate base64 literal content.
* <p>
* <code>
* Base64Literal := base64 WS ` Base64Group* [PaddedBase64Group] WS `
* <br/>
* Base64Group := WS Base64Char WS Base64Char WS Base64Char WS Base64Char
* <br/>
* PaddedBase64Group :=
*    WS Base64Char WS Base64Char WS Base64Char WS PaddingChar
*    | WS Base64Char WS Base64Char WS PaddingChar WS PaddingChar
* <br/>
* Base64Char := A .. Z | a .. z | 0 .. 9 | + | /
* <br/>
* PaddingChar := =
* <br/>
* WS := WhiteSpaceChar*
* <br/>
* WhiteSpaceChar := 0x9 | 0xA | 0xD | 0x20
* </code>
*
* @param content the string surrounded by the backticks
* @return <code>true</code>, if the string content is valid. <code>false</code> otherwise.
*/
static boolean isValidBase64LiteralContent(String content) {
char[] charArray = content.toCharArray();
int base64CharCount = 0;
int paddingCharCount = 0;
for (char c : charArray) {
switch (c) {
case LexerTerminals.TAB:
case LexerTerminals.NEWLINE:
case LexerTerminals.CARRIAGE_RETURN:
case LexerTerminals.SPACE:
break;
case LexerTerminals.EQUAL:
paddingCharCount++;
break;
default:
if (isBase64Char(c)) {
if (paddingCharCount == 0) {
base64CharCount++;
} else {
return false;
}
} else {
return false;
}
break;
}
}
if (paddingCharCount > 2) {
return false;
} else if (paddingCharCount == 0) {
return base64CharCount % 4 == 0;
} else {
return base64CharCount % 4 == 4 - paddingCharCount;
}
}
/**
* <p>
* Check whether a given char is a base64 char.
* </p>
* <code>Base64Char := A .. Z | a .. z | 0 .. 9 | + | /</code>
*
* @param c character to check
* @return <code>true</code>, if the character represents a base64 char. <code>false</code> otherwise.
*/
static boolean isBase64Char(int c) {
if ('a' <= c && c <= 'z') {
return true;
}
if ('A' <= c && c <= 'Z') {
return true;
}
if (c == '+' || c == '/') {
return true;
}
return isDigit(c);
}
static boolean isHexDigit(int c) {
if ('a' <= c && c <= 'f') {
return true;
}
if ('A' <= c && c <= 'F') {
return true;
}
return isDigit(c);
}
static boolean isDigit(int c) {
return ('0' <= c && c <= '9');
}
/**
* 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();
startToken = cloneWithDiagnosticIfListEmpty(xmlNamePattern, startToken,
DiagnosticErrorCode.ERROR_MISSING_XML_ATOMIC_NAME_PATTERN);
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)) {
return STNodeFactory.createNodeList(xmlAtomicNamePatternList);
}
STNode xmlAtomicNamePattern = parseXMLAtomicNamePattern();
xmlAtomicNamePatternList.add(xmlAtomicNamePattern);
STNode separator;
while (!isEndOfXMLNamePattern(peek().kind)) {
separator = parseXMLNamePatternSeparator();
if (separator == null) {
break;
}
xmlAtomicNamePatternList.add(separator);
xmlAtomicNamePattern = parseXMLAtomicNamePattern();
xmlAtomicNamePatternList.add(xmlAtomicNamePattern);
}
return STNodeFactory.createNodeList(xmlAtomicNamePatternList);
}
private boolean isEndOfXMLNamePattern(SyntaxKind tokenKind) {
switch (tokenKind) {
case GT_TOKEN:
case EOF_TOKEN:
return true;
case IDENTIFIER_TOKEN:
case ASTERISK_TOKEN:
case COLON_TOKEN:
default:
return false;
}
}
private STNode parseXMLNamePatternSeparator() {
STToken token = peek();
switch (token.kind) {
case PIPE_TOKEN:
return consume();
case GT_TOKEN:
case EOF_TOKEN:
return null;
default:
Solution sol = recover(token, ParserRuleContext.XML_NAME_PATTERN_RHS);
if (sol.tokenKind == SyntaxKind.GT_TOKEN) {
return null;
}
return sol.recoveredNode;
}
}
/**
* 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;
}
}
/**
* Parse match statement.
* <p>
* <code>match-stmt := match action-or-expr { match-clause+ }</code>
*
* @return Match statement
*/
private STNode parseMatchStatement() {
startContext(ParserRuleContext.MATCH_STMT);
STNode matchKeyword = parseMatchKeyword();
STNode actionOrExpr = parseActionOrExpression();
startContext(ParserRuleContext.MATCH_BODY);
STNode openBrace = parseOpenBrace();
STNode matchClauses = parseMatchClauses();
STNode closeBrace = parseCloseBrace();
endContext();
endContext();
return STNodeFactory.createMatchStatementNode(matchKeyword, actionOrExpr, openBrace, matchClauses, closeBrace);
}
/**
* Parse match keyword.
*
* @return Match keyword node
*/
private STNode parseMatchKeyword() {
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.MATCH_KEYWORD) {
return consume();
} else {
Solution sol = recover(nextToken, ParserRuleContext.MATCH_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse match clauses list.
*
* @return Match clauses list
*/
private STNode parseMatchClauses() {
List<STNode> matchClauses = new ArrayList<>();
while (!isEndOfMatchClauses(peek().kind)) {
STNode clause = parseMatchClause();
matchClauses.add(clause);
}
return STNodeFactory.createNodeList(matchClauses);
}
private boolean isEndOfMatchClauses(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case EOF_TOKEN:
case CLOSE_BRACE_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse a single match match clause.
* <p>
* <code>
* match-clause := match-pattern-list [match-guard] => block-stmt
* <br/>
* match-guard := if expression
* </code>
*
* @return A match clause
*/
private STNode parseMatchClause() {
STNode matchPatterns = parseMatchPatternList();
STNode matchGuard = parseMatchGuard();
STNode rightDoubleArrow = parseDoubleRightArrow();
STNode blockStmt = parseBlockNode();
return STNodeFactory.createMatchClauseNode(matchPatterns, matchGuard, rightDoubleArrow, blockStmt);
}
/**
* Parse match guard.
* <p>
* <code>match-guard := if expression</code>
*
* @return Match guard
*/
private STNode parseMatchGuard() {
STToken nextToken = peek();
return parseMatchGuard(nextToken.kind);
}
private STNode parseMatchGuard(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case IF_KEYWORD:
STNode ifKeyword = parseIfKeyword();
STNode expr = parseExpression(peek().kind, DEFAULT_OP_PRECEDENCE, true, false, true, false);
return STNodeFactory.createMatchGuardNode(ifKeyword, expr);
case RIGHT_DOUBLE_ARROW_TOKEN:
return STNodeFactory.createEmptyNode();
default:
Solution solution = recover(peek(), ParserRuleContext.OPTIONAL_MATCH_GUARD);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseMatchGuard(solution.tokenKind);
}
}
/**
* Parse match patterns list.
* <p>
* <code>match-pattern-list := match-pattern (| match-pattern)*</code>
*
* @return Match patterns list
*/
private STNode parseMatchPatternList() {
startContext(ParserRuleContext.MATCH_PATTERN);
List<STNode> matchClauses = new ArrayList<>();
while (!isEndOfMatchPattern(peek().kind)) {
STNode clause = parseMatchPattern();
if (clause == null) {
break;
}
matchClauses.add(clause);
STNode seperator = parseMatchPatternEnd();
if (seperator == null) {
break;
}
matchClauses.add(seperator);
}
endContext();
return STNodeFactory.createNodeList(matchClauses);
}
private boolean isEndOfMatchPattern(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case PIPE_TOKEN:
case IF_KEYWORD:
case RIGHT_ARROW_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse match pattern.
* <p>
* <code>
* match-pattern := var binding-pattern
*                  | wildcard-match-pattern
*                  | const-pattern
*                  | list-match-pattern
*                  | mapping-match-pattern
*                  | functional-match-pattern
* </code>
*
* @return Match pattern
*/
private STNode parseMatchPattern() {
STToken nextToken = peek();
return parseMatchPattern(nextToken.kind);
}
private STNode parseMatchPattern(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case OPEN_PAREN_TOKEN:
case NULL_KEYWORD:
case TRUE_KEYWORD:
case FALSE_KEYWORD:
case PLUS_TOKEN:
case MINUS_TOKEN:
case DECIMAL_INTEGER_LITERAL:
case HEX_INTEGER_LITERAL:
case DECIMAL_FLOATING_POINT_LITERAL:
case HEX_FLOATING_POINT_LITERAL:
case STRING_LITERAL:
return parseSimpleConstExpr();
case IDENTIFIER_TOKEN:
STNode typeRefOrConstExpr = parseQualifiedIdentifier(ParserRuleContext.MATCH_PATTERN);
return parseFunctionalMatchPatternOrConsPattern(typeRefOrConstExpr);
case VAR_KEYWORD:
return parseVarTypedBindingPattern();
case OPEN_BRACKET_TOKEN:
return parseListMatchPattern();
case OPEN_BRACE_TOKEN:
return parseMappingMatchPattern();
case ERROR_KEYWORD:
return parseFunctionalMatchPattern(consume());
default:
Solution solution = recover(peek(), ParserRuleContext.MATCH_PATTERN_START);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseMatchPattern(solution.tokenKind);
}
}
private STNode parseMatchPatternEnd() {
STToken nextToken = peek();
return parseMatchPatternEnd(nextToken.kind);
}
private STNode parseMatchPatternEnd(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case PIPE_TOKEN:
return parsePipeToken();
case IF_KEYWORD:
case RIGHT_DOUBLE_ARROW_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.MATCH_PATTERN_RHS);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseMatchPatternEnd(solution.tokenKind);
}
}
/**
* Parse var typed binding pattern.
* <p>
* <code>var binding-pattern</code>
* </p>
*
* @return Parsed typed binding pattern node
*/
private STNode parseVarTypedBindingPattern() {
STNode varKeyword = parseVarKeyword();
STNode bindingPattern = parseBindingPattern();
return STNodeFactory.createTypedBindingPatternNode(varKeyword, bindingPattern);
}
/**
* Parse var keyword.
*
* @return Var keyword node
*/
private STNode parseVarKeyword() {
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.VAR_KEYWORD) {
return consume();
} else {
Solution sol = recover(nextToken, ParserRuleContext.VAR_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse list match pattern.
* <p>
* <code>
*         list-match-pattern := [ list-member-match-patterns ]
*         list-member-match-patterns :=
*          match-pattern (, match-pattern)* [, rest-match-pattern]
*          | [ rest-match-pattern ]
*     </code>
* </p>
*
* @return Parsed list match pattern node
*/
private STNode parseListMatchPattern() {
startContext(ParserRuleContext.LIST_MATCH_PATTERN);
STNode openBracketToken = parseOpenBracket();
List<STNode> matchPatternList = new ArrayList<>();
STNode restMatchPattern = null;
while (!isEndOfListMatchPattern()) {
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.ELLIPSIS_TOKEN) {
restMatchPattern = parseRestMatchPattern();
break;
}
STNode matchPatternListMember = parseMatchPattern();
matchPatternList.add(matchPatternListMember);
STNode matchPatternMemberRhs = parseListMatchPatternMemberRhs();
if (matchPatternMemberRhs != null) {
matchPatternList.add(matchPatternMemberRhs);
} else {
break;
}
}
if (restMatchPattern == null) {
restMatchPattern = STNodeFactory.createEmptyNode();
}
STNode matchPatternListNode = STNodeFactory.createNodeList(matchPatternList);
STNode closeBracketToken = parseCloseBracket();
endContext();
return STNodeFactory.createListMatchPatternNode(openBracketToken, matchPatternListNode, restMatchPattern,
closeBracketToken);
}
public boolean isEndOfListMatchPattern() {
switch (peek().kind) {
case CLOSE_BRACKET_TOKEN:
case EOF_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse rest match pattern.
* <p>
* <code>
*         rest-match-pattern := ... var variable-name
*     </code>
* </p>
*
* @return Parsed rest match pattern node
*/
private STNode parseRestMatchPattern() {
startContext(ParserRuleContext.REST_MATCH_PATTERN);
STNode ellipsisToken = parseEllipsis();
STNode varKeywordToken = parseVarKeyword();
STNode variableName = parseVariableName();
endContext();
STSimpleNameReferenceNode simpleNameReferenceNode =
(STSimpleNameReferenceNode) STNodeFactory.createSimpleNameReferenceNode(variableName);
return STNodeFactory.createRestMatchPatternNode(ellipsisToken, varKeywordToken, simpleNameReferenceNode);
}
private STNode parseListMatchPatternMemberRhs() {
return parseListMatchPatternMemberRhs(peek().kind);
}
private STNode parseListMatchPatternMemberRhs(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_BRACKET_TOKEN:
case EOF_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.LIST_MATCH_PATTERN_MEMBER_RHS);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseListMatchPatternMemberRhs(solution.tokenKind);
}
}
/**
* Parse mapping match pattern.
* <p>
* mapping-match-pattern := { field-match-patterns }
* <br/>
* field-match-patterns := field-match-pattern (, field-match-pattern)* [, rest-match-pattern]
* | [ rest-match-pattern ]
* <br/>
* field-match-pattern := field-name : match-pattern
* <br/>
* rest-match-pattern := ... var variable-name
* </p>
*
* @return Parsed Node.
*/
private STNode parseMappingMatchPattern() {
startContext(ParserRuleContext.MAPPING_MATCH_PATTERN);
STNode openBraceToken = parseOpenBrace();
List<STNode> fieldMatchPatternList = new ArrayList<>();
STNode restMatchPattern = null;
boolean isEndOfFields = false;
while (!isEndOfMappingMatchPattern()) {
STNode fieldMatchPatternMember = parseFieldMatchPatternMember();
if (fieldMatchPatternMember.kind == SyntaxKind.REST_MATCH_PATTERN) {
restMatchPattern = fieldMatchPatternMember;
isEndOfFields = true;
break;
}
fieldMatchPatternList.add(fieldMatchPatternMember);
STNode fieldMatchPatternRhs = parseFieldMatchPatternRhs();
if (fieldMatchPatternRhs != null) {
fieldMatchPatternList.add(fieldMatchPatternRhs);
} else {
break;
}
}
STNode fieldMatchPatternRhs = parseFieldMatchPatternRhs();
while (isEndOfFields && fieldMatchPatternRhs != null) {
STNode invalidField = parseFieldMatchPatternMember();
restMatchPattern =
SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(restMatchPattern, fieldMatchPatternRhs);
restMatchPattern = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(restMatchPattern, invalidField);
restMatchPattern = SyntaxErrors.addDiagnostic(restMatchPattern,
DiagnosticErrorCode.ERROR_MORE_FIELD_MATCH_PATTERNS_AFTER_REST_FIELD);
fieldMatchPatternRhs = parseFieldMatchPatternRhs();
}
if (restMatchPattern == null) {
restMatchPattern = STNodeFactory.createEmptyNode();
}
STNode fieldMatchPatterns = STNodeFactory.createNodeList(fieldMatchPatternList);
STNode closeBraceToken = parseCloseBrace();
endContext();
return STNodeFactory.createMappingMatchPatternNode(openBraceToken, fieldMatchPatterns, restMatchPattern,
closeBraceToken);
}
private STNode parseFieldMatchPatternMember() {
return parseFieldMatchPatternMember(peek().kind);
}
private STNode parseFieldMatchPatternMember(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case IDENTIFIER_TOKEN:
return parseFieldMatchPattern();
case ELLIPSIS_TOKEN:
return parseRestMatchPattern();
default:
Solution solution = recover(peek(), ParserRuleContext.FIELD_MATCH_PATTERN_MEMBER);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseFieldMatchPatternMember(solution.tokenKind);
}
}
/**
* Parse filed match pattern.
* <p>
* field-match-pattern := field-name : match-pattern
* </p>
*
* @return Parsed field match pattern node
*/
public STNode parseFieldMatchPattern() {
STNode fieldNameNode = parseVariableName();
STNode colonToken = parseColon();
STNode matchPattern = parseMatchPattern();
return STNodeFactory.createFieldMatchPatternNode(fieldNameNode, colonToken, matchPattern);
}
public boolean isEndOfMappingMatchPattern() {
switch (peek().kind) {
case CLOSE_BRACE_TOKEN:
case EOF_TOKEN:
return true;
default:
return false;
}
}
private STNode parseFieldMatchPatternRhs() {
return parseFieldMatchPatternRhs(peek().kind);
}
private STNode parseFieldMatchPatternRhs(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_BRACE_TOKEN:
case EOF_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.FIELD_MATCH_PATTERN_MEMBER_RHS);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseFieldMatchPatternRhs(solution.tokenKind);
}
}
private STNode parseFunctionalMatchPatternOrConsPattern(STNode typeRefOrConstExpr) {
return parseFunctionalMatchPatternOrConsPattern(peek().kind, typeRefOrConstExpr);
}
private STNode parseFunctionalMatchPatternOrConsPattern(SyntaxKind nextToken, STNode typeRefOrConstExpr) {
switch (nextToken) {
case OPEN_PAREN_TOKEN:
return parseFunctionalMatchPattern(typeRefOrConstExpr);
default:
if (isMatchPatternEnd(peek().kind)) {
return typeRefOrConstExpr;
}
Solution solution =
recover(peek(), ParserRuleContext.FUNC_MATCH_PATTERN_OR_CONST_PATTERN, typeRefOrConstExpr);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseFunctionalMatchPatternOrConsPattern(solution.tokenKind, typeRefOrConstExpr);
}
}
private boolean isMatchPatternEnd(SyntaxKind tokenKind) {
switch (tokenKind) {
case RIGHT_DOUBLE_ARROW_TOKEN:
case COMMA_TOKEN:
case CLOSE_BRACE_TOKEN:
case CLOSE_BRACKET_TOKEN:
case CLOSE_PAREN_TOKEN:
case PIPE_TOKEN:
case IF_KEYWORD:
case EOF_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse functional match pattern.
* <p>
* functional-match-pattern := functionally-constructible-type-reference ( arg-list-match-pattern )
* <br/>
* functionally-constructible-type-reference := error | type-reference
* <br/>
* type-reference := identifier | qualified-identifier
* <br/>
* arg-list-match-pattern := positional-arg-match-patterns [, other-arg-match-patterns]
* | other-arg-match-patterns
* </p>
*
* @return Parsed functional match pattern node.
*/
private STNode parseFunctionalMatchPattern(STNode typeRef) {
startContext(ParserRuleContext.FUNCTIONAL_MATCH_PATTERN);
STNode openParenthesisToken = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
STNode argListMatchPatternNode = parseArgListMatchPatterns();
STNode closeParenthesisToken = parseCloseParenthesis();
endContext();
return STNodeFactory.createFunctionalMatchPatternNode(typeRef, openParenthesisToken, argListMatchPatternNode,
closeParenthesisToken);
}
private STNode parseArgListMatchPatterns() {
List<STNode> argListMatchPatterns = new ArrayList<>();
SyntaxKind lastValidArgKind = SyntaxKind.IDENTIFIER_TOKEN;
while (!isEndOfFunctionalMatchPattern()) {
STNode currentArg = parseArgMatchPattern();
DiagnosticErrorCode errorCode = validateArgMatchPatternOrder(lastValidArgKind, currentArg.kind);
if (errorCode == null) {
argListMatchPatterns.add(currentArg);
lastValidArgKind = currentArg.kind;
} else {
updateLastNodeInListWithInvalidNode(argListMatchPatterns, currentArg, errorCode);
}
STNode argRhs = parseArgMatchPatternRhs();
if (argRhs == null) {
break;
}
if (errorCode == null) {
argListMatchPatterns.add(argRhs);
} else {
updateLastNodeInListWithInvalidNode(argListMatchPatterns, argRhs, null);
}
}
return STNodeFactory.createNodeList(argListMatchPatterns);
}
private boolean isEndOfFunctionalMatchPattern() {
switch (peek().kind) {
case CLOSE_PAREN_TOKEN:
case EOF_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse arg match patterns.
* <code>
*     arg-match-pattern := match-pattern |   named-arg-match-pattern | rest-match-pattern
* </code>
* <br/>
* <br/>
*
* @return parsed arg match pattern node.
*/
private STNode parseArgMatchPattern() {
return parseArgMatchPattern(peek().kind);
}
private STNode parseArgMatchPattern(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case IDENTIFIER_TOKEN:
return parseNamedOrPositionalArgMatchPattern();
case ELLIPSIS_TOKEN:
return parseRestMatchPattern();
case OPEN_PAREN_TOKEN:
case NULL_KEYWORD:
case TRUE_KEYWORD:
case FALSE_KEYWORD:
case PLUS_TOKEN:
case MINUS_TOKEN:
case DECIMAL_INTEGER_LITERAL:
case HEX_INTEGER_LITERAL:
case DECIMAL_FLOATING_POINT_LITERAL:
case HEX_FLOATING_POINT_LITERAL:
case STRING_LITERAL:
case VAR_KEYWORD:
case OPEN_BRACKET_TOKEN:
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
return parseMatchPattern();
default:
Solution solution = recover(peek(), ParserRuleContext.ARG_MATCH_PATTERN);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseArgMatchPattern(solution.tokenKind);
}
}
private STNode parseNamedOrPositionalArgMatchPattern() {
STNode identifier = parseIdentifier(ParserRuleContext.MATCH_PATTERN_START);
STToken secondToken = peek();
switch (secondToken.kind) {
case EQUAL_TOKEN:
return parseNamedArgMatchPattern(identifier);
case OPEN_PAREN_TOKEN:
return parseFunctionalMatchPattern(identifier);
case COMMA_TOKEN:
case CLOSE_PAREN_TOKEN:
default:
return identifier;
}
}
/**
* Parses the next named arg match pattern.
* <br/>
* <code>named-arg-match-pattern := arg-name = match-pattern</code>
* <br/>
* <br/>
*
* @return arg match pattern list node added the new arg match pattern
*/
private STNode parseNamedArgMatchPattern(STNode identifier) {
startContext(ParserRuleContext.NAMED_ARG_MATCH_PATTERN);
STNode equalToken = parseAssignOp();
STNode matchPattern = parseMatchPattern();
endContext();
return STNodeFactory.createNamedArgMatchPatternNode(identifier, equalToken, matchPattern);
}
private STNode parseArgMatchPatternRhs() {
return parseArgMatchPatternRhs(peek().kind);
}
private STNode parseArgMatchPatternRhs(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_PAREN_TOKEN:
case EOF_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.ARG_MATCH_PATTERN_RHS);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseArgMatchPatternRhs(solution.tokenKind);
}
}
private DiagnosticErrorCode validateArgMatchPatternOrder(SyntaxKind prevArgKind, SyntaxKind currentArgKind) {
DiagnosticErrorCode errorCode = null;
switch (prevArgKind) {
case NAMED_ARG_MATCH_PATTERN:
if (currentArgKind != SyntaxKind.NAMED_ARG_MATCH_PATTERN &&
currentArgKind != SyntaxKind.REST_MATCH_PATTERN) {
errorCode = DiagnosticErrorCode.ERROR_NAMED_ARG_FOLLOWED_BY_POSITIONAL_ARG;
}
break;
case REST_MATCH_PATTERN:
errorCode = DiagnosticErrorCode.ERROR_ARG_FOLLOWED_BY_REST_ARG;
break;
default:
break;
}
return errorCode;
}
/**
* Parse markdown documentation.
*
* @return markdown documentation node
*/
private STNode parseMarkdownDocumentation() {
List<STNode> markdownDocLineList = new ArrayList<>();
STToken nextToken = peek();
while (nextToken.kind == SyntaxKind.DOCUMENTATION_STRING) {
STToken documentationString = consume();
STNode parsedDocLines = parseDocumentationString(documentationString);
appendParsedDocumentationLines(markdownDocLineList, parsedDocLines);
nextToken = peek();
}
STNode markdownDocLines = STNodeFactory.createNodeList(markdownDocLineList);
return STNodeFactory.createMarkdownDocumentationNode(markdownDocLines);
}
/**
* Parse documentation string.
*
* @return markdown documentation line list node
*/
private STNode parseDocumentationString(STToken documentationStringToken) {
List<STNode> leadingTriviaList = getLeadingTriviaList(documentationStringToken.leadingMinutiae());
TextDocument textDocument = TextDocuments.from(documentationStringToken.text());
DocumentationLexer documentationLexer =
new DocumentationLexer(textDocument.getCharacterReader(), leadingTriviaList);
AbstractTokenReader tokenReader = new TokenReader(documentationLexer);
DocumentationParser documentationParser = new DocumentationParser(tokenReader);
return documentationParser.parse();
}
private List<STNode> getLeadingTriviaList(STNode leadingMinutiaeNode) {
List<STNode> leadingTriviaList = new ArrayList<>();
int bucketCount = leadingMinutiaeNode.bucketCount();
for (int i = 0; i < bucketCount; i++) {
leadingTriviaList.add(leadingMinutiaeNode.childInBucket(i));
}
return leadingTriviaList;
}
private void appendParsedDocumentationLines(List<STNode> markdownDocLineList, STNode parsedDocLines) {
int bucketCount = parsedDocLines.bucketCount();
for (int i = 0; i < bucketCount; i++) {
STNode markdownDocLine = parsedDocLines.childInBucket(i);
markdownDocLineList.add(markdownDocLine);
}
}
/**
* Parse any statement that starts with a token that has ambiguity between being
* a type-desc or an expression.
*
* @param annots Annotations
* @return Statement node
*/
private STNode parseStmtStartsWithTypeOrExpr(SyntaxKind nextTokenKind, STNode annots) {
startContext(ParserRuleContext.AMBIGUOUS_STMT);
STNode typeOrExpr = parseTypedBindingPatternOrExpr(nextTokenKind, true);
return parseStmtStartsWithTypedBPOrExprRhs(annots, typeOrExpr);
}
private STNode parseStmtStartsWithTypedBPOrExprRhs(STNode annots, STNode typedBindingPatternOrExpr) {
if (typedBindingPatternOrExpr.kind == SyntaxKind.TYPED_BINDING_PATTERN) {
STNode finalKeyword = STNodeFactory.createEmptyNode();
switchContext(ParserRuleContext.VAR_DECL_STMT);
return parseVarDeclRhs(annots, finalKeyword, typedBindingPatternOrExpr, false);
}
STNode expr = getExpression(typedBindingPatternOrExpr);
expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, expr, false, true);
return parseStatementStartWithExprRhs(expr);
}
private STNode parseTypedBindingPatternOrExpr(boolean allowAssignment) {
STToken nextToken = peek();
return parseTypedBindingPatternOrExpr(nextToken.kind, allowAssignment);
}
private STNode parseTypedBindingPatternOrExpr(SyntaxKind nextTokenKind, boolean allowAssignment) {
STNode typeOrExpr;
switch (nextTokenKind) {
case OPEN_PAREN_TOKEN:
return parseTypedBPOrExprStartsWithOpenParenthesis();
case FUNCTION_KEYWORD:
return parseAnonFuncExprOrTypedBPWithFuncType();
case IDENTIFIER_TOKEN:
typeOrExpr = parseQualifiedIdentifier(ParserRuleContext.TYPE_NAME_OR_VAR_NAME);
return parseTypedBindingPatternOrExprRhs(typeOrExpr, allowAssignment);
case OPEN_BRACKET_TOKEN:
typeOrExpr = parseTypedDescOrExprStartsWithOpenBracket();
return parseTypedBindingPatternOrExprRhs(typeOrExpr, allowAssignment);
case NIL_LITERAL:
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:
STNode basicLiteral = parseBasicLiteral();
return parseTypedBindingPatternOrExprRhs(basicLiteral, allowAssignment);
default:
if (isValidExpressionStart(nextTokenKind, 1)) {
return parseActionOrExpressionInLhs(nextTokenKind, null);
}
return parseTypedBindingPattern(ParserRuleContext.VAR_DECL_STMT);
}
}
/**
* Parse the component after the ambiguous starting node. Ambiguous node could be either an expr
* or a type-desc. The component followed by this ambiguous node could be the binding-pattern or
* the expression-rhs.
*
* @param typeOrExpr Type desc or the expression
* @param allowAssignment Flag indicating whether to allow assignment. i.e.: whether this is a
*            valid lvalue expression
* @return Typed-binding-pattern node or an expression node
*/
private STNode parseTypedBindingPatternOrExprRhs(STNode typeOrExpr, boolean allowAssignment) {
STToken nextToken = peek();
return parseTypedBindingPatternOrExprRhs(nextToken.kind, typeOrExpr, allowAssignment);
}
private STNode parseTypedBindingPatternOrExprRhs(SyntaxKind nextTokenKind, STNode typeOrExpr,
boolean allowAssignment) {
switch (nextTokenKind) {
case PIPE_TOKEN:
STToken nextNextToken = peek(2);
if (nextNextToken.kind == SyntaxKind.EQUAL_TOKEN) {
return typeOrExpr;
}
STNode pipe = parsePipeToken();
STNode rhsTypedBPOrExpr = parseTypedBindingPatternOrExpr(allowAssignment);
if (rhsTypedBPOrExpr.kind == SyntaxKind.TYPED_BINDING_PATTERN) {
STTypedBindingPatternNode typedBP = (STTypedBindingPatternNode) rhsTypedBPOrExpr;
typeOrExpr = getTypeDescFromExpr(typeOrExpr);
STNode newTypeDesc = createUnionTypeDesc(typeOrExpr, pipe, typedBP.typeDescriptor);
return STNodeFactory.createTypedBindingPatternNode(newTypeDesc, typedBP.bindingPattern);
}
return STNodeFactory.createBinaryExpressionNode(SyntaxKind.BINARY_EXPRESSION, typeOrExpr, pipe,
rhsTypedBPOrExpr);
case BITWISE_AND_TOKEN:
nextNextToken = peek(2);
if (nextNextToken.kind == SyntaxKind.EQUAL_TOKEN) {
return typeOrExpr;
}
STNode ampersand = parseBinaryOperator();
rhsTypedBPOrExpr = parseTypedBindingPatternOrExpr(allowAssignment);
if (rhsTypedBPOrExpr.kind == SyntaxKind.TYPED_BINDING_PATTERN) {
STTypedBindingPatternNode typedBP = (STTypedBindingPatternNode) rhsTypedBPOrExpr;
typeOrExpr = getTypeDescFromExpr(typeOrExpr);
STNode newTypeDesc = createIntersectionTypeDesc(typeOrExpr, ampersand, typedBP.typeDescriptor);
return STNodeFactory.createTypedBindingPatternNode(newTypeDesc, typedBP.bindingPattern);
}
return STNodeFactory.createBinaryExpressionNode(SyntaxKind.BINARY_EXPRESSION, typeOrExpr, ampersand,
rhsTypedBPOrExpr);
case SEMICOLON_TOKEN:
if (isDefiniteExpr(typeOrExpr.kind)) {
return typeOrExpr;
}
if (isDefiniteTypeDesc(typeOrExpr.kind) || !isAllBasicLiterals(typeOrExpr)) {
STNode typeDesc = getTypeDescFromExpr(typeOrExpr);
return parseTypeBindingPatternStartsWithAmbiguousNode(typeDesc);
}
return typeOrExpr;
case IDENTIFIER_TOKEN:
case QUESTION_MARK_TOKEN:
if (isAmbiguous(typeOrExpr) || isDefiniteTypeDesc(typeOrExpr.kind)) {
STNode typeDesc = getTypeDescFromExpr(typeOrExpr);
return parseTypeBindingPatternStartsWithAmbiguousNode(typeDesc);
}
return typeOrExpr;
case EQUAL_TOKEN:
return typeOrExpr;
case OPEN_BRACKET_TOKEN:
return parseTypedBindingPatternOrMemberAccess(typeOrExpr, false, allowAssignment,
ParserRuleContext.AMBIGUOUS_STMT);
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
STNode typeDesc = getTypeDescFromExpr(typeOrExpr);
return parseTypeBindingPatternStartsWithAmbiguousNode(typeDesc);
default:
if (isCompoundBinaryOperator(nextTokenKind)) {
return typeOrExpr;
}
if (isValidExprRhsStart(nextTokenKind, typeOrExpr.kind)) {
return typeOrExpr;
}
STToken token = peek();
Solution solution =
recover(token, ParserRuleContext.BINDING_PATTERN_OR_EXPR_RHS, typeOrExpr, allowAssignment);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseTypedBindingPatternOrExprRhs(solution.tokenKind, typeOrExpr, allowAssignment);
}
}
private STNode parseTypeBindingPatternStartsWithAmbiguousNode(STNode typeDesc) {
startContext(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
typeDesc = parseComplexTypeDescriptor(typeDesc, ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN, false);
endContext();
return parseTypedBindingPatternTypeRhs(typeDesc, ParserRuleContext.VAR_DECL_STMT);
}
private STNode parseTypedBPOrExprStartsWithOpenParenthesis() {
STNode exprOrTypeDesc = parseTypedDescOrExprStartsWithOpenParenthesis();
if (isDefiniteTypeDesc(exprOrTypeDesc.kind)) {
return parseTypeBindingPatternStartsWithAmbiguousNode(exprOrTypeDesc);
}
return parseTypedBindingPatternOrExprRhs(exprOrTypeDesc, false);
}
private boolean isDefiniteTypeDesc(SyntaxKind kind) {
return kind.compareTo(SyntaxKind.TYPE_DESC) >= 0 && kind.compareTo(SyntaxKind.SINGLETON_TYPE_DESC) <= 0;
}
private boolean isDefiniteExpr(SyntaxKind kind) {
if (kind == SyntaxKind.QUALIFIED_NAME_REFERENCE || kind == SyntaxKind.SIMPLE_NAME_REFERENCE) {
return false;
}
return kind.compareTo(SyntaxKind.BINARY_EXPRESSION) >= 0 && kind.compareTo(SyntaxKind.FAIL_EXPRESSION) <= 0;
}
/**
* Parse type or expression that starts with open parenthesis. Possible options are:
* 1) () - nil type-desc or nil-literal
* 2) (T) - Parenthesized type-desc
* 3) (expr) - Parenthesized expression
* 4) (param, param, ..) - Anon function params
*
* @return Type-desc or expression node
*/
private STNode parseTypedDescOrExprStartsWithOpenParenthesis() {
STNode openParen = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.CLOSE_PAREN_TOKEN) {
STNode closeParen = parseCloseParenthesis();
return parseTypeOrExprStartWithEmptyParenthesis(openParen, closeParen);
}
STNode typeOrExpr = parseTypeDescOrExpr();
if (isAction(typeOrExpr)) {
STNode closeParen = parseCloseParenthesis();
return STNodeFactory.createBracedExpressionNode(SyntaxKind.BRACED_ACTION, openParen, typeOrExpr,
closeParen);
}
if (isExpression(typeOrExpr.kind)) {
startContext(ParserRuleContext.BRACED_EXPR_OR_ANON_FUNC_PARAMS);
return parseBracedExprOrAnonFuncParamRhs(peek().kind, openParen, typeOrExpr, false);
}
STNode closeParen = parseCloseParenthesis();
return STNodeFactory.createParenthesisedTypeDescriptorNode(openParen, typeOrExpr, closeParen);
}
/**
* Parse type-desc or expression. This method does not handle binding patterns.
*
* @return Type-desc node or expression node
*/
private STNode parseTypeDescOrExpr() {
STToken nextToken = peek();
STNode typeOrExpr;
switch (nextToken.kind) {
case OPEN_PAREN_TOKEN:
typeOrExpr = parseTypedDescOrExprStartsWithOpenParenthesis();
break;
case FUNCTION_KEYWORD:
typeOrExpr = parseAnonFuncExprOrFuncTypeDesc();
break;
case IDENTIFIER_TOKEN:
typeOrExpr = parseQualifiedIdentifier(ParserRuleContext.TYPE_NAME_OR_VAR_NAME);
return parseTypeDescOrExprRhs(typeOrExpr);
case OPEN_BRACKET_TOKEN:
typeOrExpr = parseTypedDescOrExprStartsWithOpenBracket();
break;
case NIL_LITERAL:
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:
STNode basicLiteral = parseBasicLiteral();
return parseTypeDescOrExprRhs(basicLiteral);
default:
if (isValidExpressionStart(nextToken.kind, 1)) {
return parseActionOrExpressionInLhs(nextToken.kind, null);
}
return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
}
if (isDefiniteTypeDesc(typeOrExpr.kind)) {
return parseComplexTypeDescriptor(typeOrExpr, ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN, true);
}
return parseTypeDescOrExprRhs(typeOrExpr);
}
private boolean isExpression(SyntaxKind kind) {
switch (kind) {
case BASIC_LITERAL:
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:
return true;
default:
return kind.compareTo(SyntaxKind.BINARY_EXPRESSION) >= 0 &&
kind.compareTo(SyntaxKind.FAIL_EXPRESSION) <= 0;
}
}
/**
* Parse statement that starts with an empty parenthesis. Empty parenthesis can be
* 1) Nil literal
* 2) Nil type-desc
* 3) Anon-function params
*
* @param openParen Open parenthesis
* @param closeParen Close parenthesis
* @return Parsed node
*/
private STNode parseTypeOrExprStartWithEmptyParenthesis(STNode openParen, STNode closeParen) {
STToken nextToken = peek();
switch (nextToken.kind) {
case RIGHT_DOUBLE_ARROW_TOKEN:
STNode params = STNodeFactory.createNodeList();
STNode anonFuncParam =
STNodeFactory.createImplicitAnonymousFunctionParameters(openParen, params, closeParen);
endContext();
return anonFuncParam;
default:
return STNodeFactory.createNilLiteralNode(openParen, closeParen);
}
}
private STNode parseAnonFuncExprOrTypedBPWithFuncType() {
STNode exprOrTypeDesc = parseAnonFuncExprOrFuncTypeDesc();
if (isAction(exprOrTypeDesc) || isExpression(exprOrTypeDesc.kind)) {
return exprOrTypeDesc;
}
return parseTypedBindingPatternTypeRhs(exprOrTypeDesc, ParserRuleContext.VAR_DECL_STMT);
}
/**
* Parse anon-func-expr or function-type-desc, by resolving the ambiguity.
*
* @return Anon-func-expr or function-type-desc
*/
private STNode parseAnonFuncExprOrFuncTypeDesc() {
startContext(ParserRuleContext.FUNC_TYPE_DESC_OR_ANON_FUNC);
STNode functionKeyword = parseFunctionKeyword();
STNode funcSignature = parseFuncSignature(true);
endContext();
switch (peek().kind) {
case OPEN_BRACE_TOKEN:
case RIGHT_DOUBLE_ARROW_TOKEN:
switchContext(ParserRuleContext.EXPRESSION_STATEMENT);
startContext(ParserRuleContext.ANON_FUNC_EXPRESSION);
funcSignature = validateAndGetFuncParams((STFunctionSignatureNode) funcSignature);
STNode funcBody = parseAnonFuncBody(false);
STNode annots = STNodeFactory.createEmptyNodeList();
STNode anonFunc = STNodeFactory.createExplicitAnonymousFunctionExpressionNode(annots, functionKeyword,
funcSignature, funcBody);
return parseExpressionRhs(DEFAULT_OP_PRECEDENCE, anonFunc, false, true);
case IDENTIFIER_TOKEN:
default:
switchContext(ParserRuleContext.VAR_DECL_STMT);
STNode funcTypeDesc = STNodeFactory.createFunctionTypeDescriptorNode(functionKeyword, funcSignature);
return parseComplexTypeDescriptor(funcTypeDesc, ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN,
true);
}
}
private STNode parseTypeDescOrExprRhs(STNode typeOrExpr) {
SyntaxKind nextTokenKind = peek().kind;
return parseTypeDescOrExprRhs(nextTokenKind, typeOrExpr);
}
private STNode parseTypeDescOrExprRhs(SyntaxKind nextTokenKind, STNode typeOrExpr) {
STNode typeDesc;
switch (nextTokenKind) {
case PIPE_TOKEN:
STToken nextNextToken = peek(2);
if (nextNextToken.kind == SyntaxKind.EQUAL_TOKEN) {
return typeOrExpr;
}
STNode pipe = parsePipeToken();
STNode rhsTypeDescOrExpr = parseTypeDescOrExpr();
if (isExpression(rhsTypeDescOrExpr.kind)) {
return STNodeFactory.createBinaryExpressionNode(SyntaxKind.BINARY_EXPRESSION, typeOrExpr, pipe,
rhsTypeDescOrExpr);
}
typeDesc = getTypeDescFromExpr(typeOrExpr);
rhsTypeDescOrExpr = getTypeDescFromExpr(rhsTypeDescOrExpr);
return createUnionTypeDesc(typeDesc, pipe, rhsTypeDescOrExpr);
case BITWISE_AND_TOKEN:
nextNextToken = peek(2);
if (nextNextToken.kind != SyntaxKind.EQUAL_TOKEN) {
return typeOrExpr;
}
STNode ampersand = parseBinaryOperator();
rhsTypeDescOrExpr = parseTypeDescOrExpr();
if (isExpression(rhsTypeDescOrExpr.kind)) {
return STNodeFactory.createBinaryExpressionNode(SyntaxKind.BINARY_EXPRESSION, typeOrExpr, ampersand,
rhsTypeDescOrExpr);
}
typeDesc = getTypeDescFromExpr(typeOrExpr);
rhsTypeDescOrExpr = getTypeDescFromExpr(rhsTypeDescOrExpr);
return createIntersectionTypeDesc(typeDesc, ampersand, rhsTypeDescOrExpr);
case IDENTIFIER_TOKEN:
case QUESTION_MARK_TOKEN:
startContext(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
typeDesc = parseComplexTypeDescriptor(typeOrExpr, ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN,
false);
endContext();
return typeDesc;
case SEMICOLON_TOKEN:
return getTypeDescFromExpr(typeOrExpr);
case EQUAL_TOKEN:
case CLOSE_PAREN_TOKEN:
case CLOSE_BRACE_TOKEN:
case CLOSE_BRACKET_TOKEN:
case EOF_TOKEN:
case COMMA_TOKEN:
return typeOrExpr;
case OPEN_BRACKET_TOKEN:
return parseTypedBindingPatternOrMemberAccess(typeOrExpr, false, true,
ParserRuleContext.AMBIGUOUS_STMT);
case ELLIPSIS_TOKEN:
STNode ellipsis = parseEllipsis();
typeOrExpr = getTypeDescFromExpr(typeOrExpr);
return STNodeFactory.createRestDescriptorNode(typeOrExpr, ellipsis);
default:
if (isCompoundBinaryOperator(nextTokenKind)) {
return typeOrExpr;
}
if (isValidExprRhsStart(nextTokenKind, typeOrExpr.kind)) {
return parseExpressionRhs(nextTokenKind, DEFAULT_OP_PRECEDENCE, typeOrExpr, false, false, false,
false);
}
STToken token = peek();
Solution solution = recover(token, ParserRuleContext.BINDING_PATTERN_OR_EXPR_RHS, typeOrExpr);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseTypeDescOrExprRhs(solution.tokenKind, typeOrExpr);
}
}
private boolean isAmbiguous(STNode node) {
switch (node.kind) {
case SIMPLE_NAME_REFERENCE:
case QUALIFIED_NAME_REFERENCE:
case NIL_LITERAL:
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 BRACKETED_LIST:
return true;
case BINARY_EXPRESSION:
STBinaryExpressionNode binaryExpr = (STBinaryExpressionNode) node;
if (binaryExpr.operator.kind != SyntaxKind.PIPE_TOKEN ||
binaryExpr.operator.kind == SyntaxKind.BITWISE_AND_TOKEN) {
return false;
}
return isAmbiguous(binaryExpr.lhsExpr) && isAmbiguous(binaryExpr.rhsExpr);
case BRACED_EXPRESSION:
return isAmbiguous(((STBracedExpressionNode) node).expression);
case INDEXED_EXPRESSION:
STIndexedExpressionNode indexExpr = (STIndexedExpressionNode) node;
if (!isAmbiguous(indexExpr.containerExpression)) {
return false;
}
STNode keys = indexExpr.keyExpression;
for (int i = 0; i < keys.bucketCount(); i++) {
STNode item = keys.childInBucket(i);
if (item.kind == SyntaxKind.COMMA_TOKEN) {
continue;
}
if (!isAmbiguous(item)) {
return false;
}
}
return true;
default:
return false;
}
}
private boolean isAllBasicLiterals(STNode node) {
switch (node.kind) {
case NIL_LITERAL:
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:
return true;
case BINARY_EXPRESSION:
STBinaryExpressionNode binaryExpr = (STBinaryExpressionNode) node;
if (binaryExpr.operator.kind != SyntaxKind.PIPE_TOKEN ||
binaryExpr.operator.kind == SyntaxKind.BITWISE_AND_TOKEN) {
return false;
}
return isAmbiguous(binaryExpr.lhsExpr) && isAmbiguous(binaryExpr.rhsExpr);
case BRACED_EXPRESSION:
return isAmbiguous(((STBracedExpressionNode) node).expression);
case BRACKETED_LIST:
STAmbiguousCollectionNode list = (STAmbiguousCollectionNode) node;
for (STNode member : list.members) {
if (member.kind == SyntaxKind.COMMA_TOKEN) {
continue;
}
if (!isAllBasicLiterals(member)) {
return false;
}
}
return true;
case UNARY_EXPRESSION:
STUnaryExpressionNode unaryExpr = (STUnaryExpressionNode) node;
if (unaryExpr.unaryOperator.kind != SyntaxKind.PLUS_TOKEN &&
unaryExpr.unaryOperator.kind != SyntaxKind.MINUS_TOKEN) {
return false;
}
return isNumericLiteral(unaryExpr.expression);
default:
return false;
}
}
private boolean isNumericLiteral(STNode node) {
switch (node.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 STNode parseTypedDescOrExprStartsWithOpenBracket() {
startContext(ParserRuleContext.BRACKETED_LIST);
STNode openBracket = parseOpenBracket();
List<STNode> members = new ArrayList<>();
STNode memberEnd;
while (!isEndOfListConstructor(peek().kind)) {
STNode expr = parseTypeDescOrExpr();
members.add(expr);
memberEnd = parseBracketedListMemberEnd();
if (memberEnd == null) {
break;
}
members.add(memberEnd);
}
STNode memberNodes = STNodeFactory.createNodeList(members);
STNode closeBracket = parseCloseBracket();
endContext();
return STNodeFactory.createTupleTypeDescriptorNode(openBracket, memberNodes, closeBracket);
}
/**
* 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 parseBindingPatternStartsWithIdentifier();
case OPEN_BRACE_TOKEN:
return parseMappingBindingPattern();
case ERROR_KEYWORD:
return parseErrorBindingPattern();
default:
Solution sol = recover(peek(), ParserRuleContext.BINDING_PATTERN);
if (sol.action == Action.REMOVE) {
return sol.recoveredNode;
}
return parseBindingPattern(sol.tokenKind);
}
}
private STNode parseBindingPatternStartsWithIdentifier() {
STNode argNameOrBindingPattern =
parseQualifiedIdentifier(ParserRuleContext.BINDING_PATTERN_STARTING_IDENTIFIER);
STToken secondToken = peek();
if (secondToken.kind == SyntaxKind.OPEN_PAREN_TOKEN) {
startContext(ParserRuleContext.FUNCTIONAL_BINDING_PATTERN);
return parseFunctionalBindingPattern(argNameOrBindingPattern);
}
if (argNameOrBindingPattern.kind != SyntaxKind.SIMPLE_NAME_REFERENCE) {
STNode identifier = SyntaxErrors.createMissingTokenWithDiagnostics(SyntaxKind.IDENTIFIER_TOKEN);
identifier = SyntaxErrors.cloneWithLeadingInvalidNodeMinutiae(identifier, argNameOrBindingPattern);
return createCaptureOrWildcardBP(identifier);
}
return createCaptureOrWildcardBP(((STSimpleNameReferenceNode) argNameOrBindingPattern).name);
}
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) {
if (isEndOfListBindingPattern(peek().kind) && bindingPatternsList.size() == 0) {
STNode closeBracket = parseCloseBracket();
STNode restBindingPattern = STNodeFactory.createEmptyNode();
STNode bindingPatternsNode = STNodeFactory.createNodeList(bindingPatternsList);
return STNodeFactory.createListBindingPatternNode(openBracket, bindingPatternsNode, restBindingPattern,
closeBracket);
}
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 = parseListBindingPatternMemberRhs(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 parseListBindingPatternMemberRhs() {
return parseListBindingPatternMemberRhs(peek().kind);
}
private STNode parseListBindingPatternMemberRhs(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_BRACKET_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.LIST_BINDING_PATTERN_MEMBER_END);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseListBindingPatternMemberRhs(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 member.
* <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 member
*/
private STNode parseListBindingPatternMember() {
STToken token = peek();
return parseListBindingPatternMember(token.kind);
}
private STNode parseListBindingPatternMember(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case ELLIPSIS_TOKEN:
return parseRestBindingPattern();
case OPEN_BRACKET_TOKEN:
case IDENTIFIER_TOKEN:
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
return parseBindingPattern();
default:
Solution sol = recover(peek(), ParserRuleContext.LIST_BINDING_PATTERN_MEMBER);
if (sol.action == Action.REMOVE) {
return sol.recoveredNode;
}
return parseListBindingPatternMember(sol.tokenKind);
}
}
/**
* Parse rest binding pattern.
* <p>
* <code>
* rest-binding-pattern := ... variable-name
* </code>
*
* @return Rest binding pattern node
*/
private STNode parseRestBindingPattern() {
startContext(ParserRuleContext.REST_BINDING_PATTERN);
STNode ellipsis = parseEllipsis();
STNode varName = parseVariableName();
endContext();
STSimpleNameReferenceNode simpleNameReferenceNode =
(STSimpleNameReferenceNode) STNodeFactory.createSimpleNameReferenceNode(varName);
return STNodeFactory.createRestBindingPatternNode(ellipsis, simpleNameReferenceNode);
}
/**
* 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, false);
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);
STNode openBrace = parseOpenBrace();
STToken token = peek();
if (isEndOfMappingBindingPattern(token.kind)) {
STNode closeBrace = parseCloseBrace();
STNode bindingPatternsNode = STNodeFactory.createEmptyNodeList();
STNode restBindingPattern = STNodeFactory.createEmptyNode();
endContext();
return STNodeFactory.createMappingBindingPatternNode(openBrace, bindingPatternsNode, restBindingPattern,
closeBrace);
}
List<STNode> bindingPatterns = new ArrayList<>();
STNode prevMember = parseMappingBindingPatternMember();
if (prevMember.kind != SyntaxKind.REST_BINDING_PATTERN) {
bindingPatterns.add(prevMember);
}
return parseMappingBindingPattern(openBrace, bindingPatterns, prevMember);
}
private STNode parseMappingBindingPattern(STNode openBrace, List<STNode> bindingPatterns, STNode prevMember) {
STToken token = peek();
STNode mappingBindingPatternRhs = null;
while (!isEndOfMappingBindingPattern(token.kind) && prevMember.kind != SyntaxKind.REST_BINDING_PATTERN) {
mappingBindingPatternRhs = parseMappingBindingPatternEnd(token.kind);
if (mappingBindingPatternRhs == null) {
break;
}
bindingPatterns.add(mappingBindingPatternRhs);
prevMember = parseMappingBindingPatternMember();
if (prevMember.kind == SyntaxKind.REST_BINDING_PATTERN) {
break;
}
bindingPatterns.add(prevMember);
token = peek();
}
STNode restBindingPattern;
if (prevMember.kind == SyntaxKind.REST_BINDING_PATTERN) {
restBindingPattern = prevMember;
} 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) {
STNode simpleNameReference = STNodeFactory.createSimpleNameReferenceNode(identifier);
if (peek().kind != SyntaxKind.COLON_TOKEN) {
return STNodeFactory.createFieldBindingPatternVarnameNode(simpleNameReference);
}
STNode colon = parseColon();
STNode bindingPattern = parseBindingPattern();
return STNodeFactory.createFieldBindingPatternFullNode(simpleNameReference, colon, bindingPattern);
}
private boolean isEndOfMappingBindingPattern(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case CLOSE_BRACE_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse error binding pattern node.
* <p>
* <code>functional-binding-pattern := error ( arg-list-binding-pattern )</code>
*
* @return Error binding pattern node.
*/
private STNode parseErrorBindingPattern() {
startContext(ParserRuleContext.FUNCTIONAL_BINDING_PATTERN);
STNode typeDesc = parseErrorKeyword();
return parseFunctionalBindingPattern(typeDesc);
}
/**
* Parse functional binding pattern.
* <p>
* <code>
* functional-binding-pattern := functionally-constructible-type-reference ( arg-list-binding-pattern )
* <br/><br/>
* functionally-constructible-type-reference := error | type-reference
* </code>
*
* @param typeDesc Functionally constructible type reference
* @return Functional binding pattern node.
*/
private STNode parseFunctionalBindingPattern(STNode typeDesc) {
STNode openParenthesis = parseOpenParenthesis(ParserRuleContext.ARG_LIST_START);
STNode argListBindingPatterns = parseArgListBindingPatterns();
STNode closeParenthesis = parseCloseParenthesis();
endContext();
return STNodeFactory.createFunctionalBindingPatternNode(typeDesc, openParenthesis, argListBindingPatterns,
closeParenthesis);
}
private STNode parseArgListBindingPatterns() {
List<STNode> argListBindingPatterns = new ArrayList<>();
SyntaxKind lastValidArgKind = SyntaxKind.CAPTURE_BINDING_PATTERN;
STToken nextToken = peek();
if (isEndOfParametersList(nextToken.kind)) {
return STNodeFactory.createNodeList(argListBindingPatterns);
}
argListBindingPatterns.add(parseArgBindingPattern());
nextToken = peek();
while (!isEndOfParametersList(nextToken.kind)) {
STNode argEnd = parseArgsBindingPatternEnd(nextToken.kind);
if (argEnd == null) {
break;
}
nextToken = peek();
STNode currentArg = parseArgBindingPattern(nextToken.kind);
DiagnosticErrorCode errorCode = validateArgBindingPatternOrder(lastValidArgKind, currentArg.kind);
if (errorCode == null) {
argListBindingPatterns.add(argEnd);
argListBindingPatterns.add(currentArg);
lastValidArgKind = currentArg.kind;
} else {
updateLastNodeInListWithInvalidNode(argListBindingPatterns, argEnd, null);
updateLastNodeInListWithInvalidNode(argListBindingPatterns, currentArg, errorCode);
}
nextToken = peek();
}
return STNodeFactory.createNodeList(argListBindingPatterns);
}
private STNode parseArgsBindingPatternEnd() {
STToken nextToken = peek();
return parseArgsBindingPatternEnd(nextToken.kind);
}
private STNode parseArgsBindingPatternEnd(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_PAREN_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.ARG_BINDING_PATTERN_END);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseArgsBindingPatternEnd(solution.tokenKind);
}
}
/**
* Parse arg binding pattern.
* <p>
* <code>
* arg-list-binding-pattern := positional-arg-binding-patterns [, other-arg-binding-patterns]
*                             | other-arg-binding-patterns
* <br/><br/>
* positional-arg-binding-patterns := positional-arg-binding-pattern (, positional-arg-binding-pattern)*
* <br/><br/>
* positional-arg-binding-pattern := binding-pattern
* <br/><br/>
* other-arg-binding-patterns := named-arg-binding-patterns [, rest-binding-pattern] | [rest-binding-pattern]
* <br/><br/>
* named-arg-binding-patterns := named-arg-binding-pattern (, named-arg-binding-pattern)*
* <br/><br/>
* named-arg-binding-pattern := arg-name = binding-pattern
* </code>
*
* @return Arg binding pattern
*/
private STNode parseArgBindingPattern() {
STToken nextToken = peek();
return parseArgBindingPattern(nextToken.kind);
}
private STNode parseArgBindingPattern(SyntaxKind kind) {
switch (kind) {
case ELLIPSIS_TOKEN:
return parseRestBindingPattern();
case IDENTIFIER_TOKEN:
return parseNamedOrPositionalArgBindingPattern(kind);
case OPEN_BRACKET_TOKEN:
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
return parseBindingPattern();
default:
Solution solution = recover(peek(), ParserRuleContext.ARG_BINDING_PATTERN);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseArgBindingPattern(solution.tokenKind);
}
}
private STNode parseNamedOrPositionalArgBindingPattern(SyntaxKind nextTokenKind) {
STNode argNameOrBindingPattern = parseQualifiedIdentifier(ParserRuleContext.ARG_BINDING_PATTERN_START_IDENT);
STToken secondToken = peek();
switch (secondToken.kind) {
case EQUAL_TOKEN:
STNode equal = parseAssignOp();
STNode bindingPattern = parseBindingPattern();
return STNodeFactory.createNamedArgBindingPatternNode(argNameOrBindingPattern, equal, bindingPattern);
case OPEN_PAREN_TOKEN:
return parseFunctionalBindingPattern(argNameOrBindingPattern);
case COMMA_TOKEN:
case CLOSE_PAREN_TOKEN:
default:
return createCaptureOrWildcardBP(argNameOrBindingPattern);
}
}
private DiagnosticErrorCode validateArgBindingPatternOrder(SyntaxKind prevArgKind, SyntaxKind currentArgKind) {
DiagnosticErrorCode errorCode = null;
switch (prevArgKind) {
case CAPTURE_BINDING_PATTERN:
case LIST_BINDING_PATTERN:
case MAPPING_BINDING_PATTERN:
break;
case NAMED_ARG_BINDING_PATTERN:
if (currentArgKind != SyntaxKind.NAMED_ARG_BINDING_PATTERN &&
currentArgKind != SyntaxKind.REST_BINDING_PATTERN) {
errorCode = DiagnosticErrorCode.ERROR_NAMED_ARG_FOLLOWED_BY_POSITIONAL_ARG;
}
break;
case REST_BINDING_PATTERN:
errorCode = DiagnosticErrorCode.ERROR_ARG_FOLLOWED_BY_REST_ARG;
break;
default:
throw new IllegalStateException("Invalid SyntaxKind in an argument");
}
return errorCode;
}
/*
* 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) {
STToken nextToken = peek();
return parseTypedBindingPatternTypeRhs(nextToken.kind, typeDesc, context, true);
}
private STNode parseTypedBindingPatternTypeRhs(STNode typeDesc, ParserRuleContext context, boolean isRoot) {
STToken nextToken = peek();
return parseTypedBindingPatternTypeRhs(nextToken.kind, typeDesc, context, isRoot);
}
private STNode parseTypedBindingPatternTypeRhs(SyntaxKind nextTokenKind, STNode typeDesc, ParserRuleContext context,
boolean isRoot) {
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, true, context);
assert typedBindingPattern.kind == SyntaxKind.TYPED_BINDING_PATTERN;
return typedBindingPattern;
case CLOSE_PAREN_TOKEN:
case COMMA_TOKEN:
case CLOSE_BRACKET_TOKEN:
case CLOSE_BRACE_TOKEN:
if (!isRoot) {
return typeDesc;
}
default:
Solution solution =
recover(peek(), ParserRuleContext.TYPED_BINDING_PATTERN_TYPE_RHS, typeDesc, context, isRoot);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseTypedBindingPatternTypeRhs(solution.tokenKind, typeDesc, context, isRoot);
}
}
/**
* 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,
boolean allowAssignment, 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, false);
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, allowAssignment, context);
}
private STNode parseAsMemberAccessExpr(STNode typeNameOrExpr, STNode openBracket, STNode member) {
member = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, member, false, true);
STNode closeBracket = parseCloseBracket();
endContext();
STNode keyExpr = STNodeFactory.createNodeList(member);
STNode memberAccessExpr =
STNodeFactory.createIndexedExpressionNode(typeNameOrExpr, openBracket, keyExpr, closeBracket);
return parseExpressionRhs(DEFAULT_OP_PRECEDENCE, memberAccessExpr, false, 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:
case STRING_LITERAL:
return parseBasicLiteral();
case CLOSE_BRACKET_TOKEN:
return STNodeFactory.createEmptyNode();
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
case ELLIPSIS_TOKEN:
case OPEN_BRACKET_TOKEN:
return parseStatementStartBracketedListMember();
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);
typeDesc = validateForUsageOfVar(typeDesc);
STNode closeBracket = parseCloseBracket();
endContext();
return parseTypedBindingPatternOrMemberAccessRhs(typeDesc, openBracket, member, closeBracket, true, 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,
boolean allowAssignment, ParserRuleContext context) {
STToken nextToken = peek();
return parseTypedBindingPatternOrMemberAccessRhs(nextToken.kind, typeDescOrExpr, openBracket, member,
closeBracket, isTypedBindingPattern, allowAssignment, context);
}
private STNode parseTypedBindingPatternOrMemberAccessRhs(SyntaxKind nextTokenKind, STNode typeDescOrExpr,
STNode openBracket, STNode member, STNode closeBracket,
boolean isTypedBindingPattern, boolean allowAssignment,
ParserRuleContext context) {
switch (nextTokenKind) {
case IDENTIFIER_TOKEN:
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
STNode typeDesc = getTypeDescFromExpr(typeDescOrExpr);
STNode arrayTypeDesc = getArrayTypeDesc(openBracket, member, closeBracket, typeDesc);
return parseTypedBindingPatternTypeRhs(arrayTypeDesc, context);
case OPEN_BRACKET_TOKEN:
if (isTypedBindingPattern) {
typeDesc = getTypeDescFromExpr(typeDescOrExpr);
arrayTypeDesc = createArrayTypeDesc(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, allowAssignment, context);
case QUESTION_MARK_TOKEN:
typeDesc = getTypeDescFromExpr(typeDescOrExpr);
arrayTypeDesc = getArrayTypeDesc(openBracket, member, closeBracket, typeDesc);
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, isTypedBindingPattern);
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 || !allowAssignment || !isValidLVExpr(typeDescOrExpr)) {
return createTypedBindingPattern(typeDescOrExpr, openBracket, member, closeBracket);
}
keyExpr = STNodeFactory.createNodeList(member);
typeDescOrExpr = getExpression(typeDescOrExpr);
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, closeBracket.kind)) {
keyExpr = STNodeFactory.createNodeList(member);
typeDescOrExpr = getExpression(typeDescOrExpr);
return STNodeFactory.createIndexedExpressionNode(typeDescOrExpr, openBracket, keyExpr,
closeBracket);
}
break;
}
Solution solution = recover(peek(), ParserRuleContext.BRACKETED_LIST_RHS, typeDescOrExpr, openBracket, member,
closeBracket, isTypedBindingPattern, allowAssignment, context);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseTypedBindingPatternOrMemberAccessRhs(solution.tokenKind, typeDescOrExpr, openBracket, member,
closeBracket, isTypedBindingPattern, allowAssignment, context);
}
private STNode createTypedBindingPattern(STNode typeDescOrExpr, STNode openBracket, STNode member,
STNode closeBracket) {
STNode bindingPatterns;
if (isEmpty(member)) {
bindingPatterns = STNodeFactory.createEmptyNodeList();
} else {
STNode bindingPattern = getBindingPattern(member);
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-desc/binary-expression that involves ambiguous
* bracketed list 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,
boolean isTypedBindingPattern) {
STNode pipeOrAndToken = parseUnionOrIntersectionToken();
STNode typedBindingPatternOrExpr = parseTypedBindingPatternOrExpr(false);
if (isTypedBindingPattern || typedBindingPatternOrExpr.kind == SyntaxKind.TYPED_BINDING_PATTERN) {
STNode lhsTypeDesc = getTypeDescFromExpr(typeDescOrExpr);
lhsTypeDesc = getArrayTypeDesc(openBracket, member, closeBracket, lhsTypeDesc);
STTypedBindingPatternNode rhsTypedBindingPattern = (STTypedBindingPatternNode) typedBindingPatternOrExpr;
STNode newTypeDesc;
if (pipeOrAndToken.kind == SyntaxKind.PIPE_TOKEN) {
newTypeDesc = createUnionTypeDesc(lhsTypeDesc, pipeOrAndToken, rhsTypedBindingPattern.typeDescriptor);
} else {
newTypeDesc =
createIntersectionTypeDesc(lhsTypeDesc, pipeOrAndToken, rhsTypedBindingPattern.typeDescriptor);
}
return STNodeFactory.createTypedBindingPatternNode(newTypeDesc, rhsTypedBindingPattern.bindingPattern);
} else {
STNode keyExpr = getExpression(member);
STNode containerExpr = getExpression(typeDescOrExpr);
STNode lhsExpr =
STNodeFactory.createIndexedExpressionNode(containerExpr, openBracket, keyExpr, closeBracket);
return STNodeFactory.createBinaryExpressionNode(SyntaxKind.BINARY_EXPRESSION, lhsExpr, pipeOrAndToken,
typedBindingPatternOrExpr);
}
}
private STNode getArrayTypeDesc(STNode openBracket, STNode member, STNode closeBracket, STNode lhsTypeDesc) {
if (lhsTypeDesc.kind == SyntaxKind.UNION_TYPE_DESC) {
STUnionTypeDescriptorNode unionTypeDesc = (STUnionTypeDescriptorNode) lhsTypeDesc;
STNode middleTypeDesc = getArrayTypeDesc(openBracket, member, closeBracket, unionTypeDesc.rightTypeDesc);
lhsTypeDesc = createUnionTypeDesc(unionTypeDesc.leftTypeDesc, unionTypeDesc.pipeToken, middleTypeDesc);
} else if (lhsTypeDesc.kind == SyntaxKind.INTERSECTION_TYPE_DESC) {
STIntersectionTypeDescriptorNode intersectionTypeDesc = (STIntersectionTypeDescriptorNode) lhsTypeDesc;
STNode middleTypeDesc =
getArrayTypeDesc(openBracket, member, closeBracket, intersectionTypeDesc.rightTypeDesc);
lhsTypeDesc = createIntersectionTypeDesc(intersectionTypeDesc.leftTypeDesc,
intersectionTypeDesc.bitwiseAndToken, middleTypeDesc);
} else {
lhsTypeDesc = createArrayTypeDesc(lhsTypeDesc, openBracket, member, closeBracket);
}
return lhsTypeDesc;
}
/**
* 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 (isDefiniteTypeDesc(memberNode.kind)) {
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:
case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
return SyntaxKind.NONE;
default:
return SyntaxKind.INDEXED_EXPRESSION;
}
}
/*
* 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);
return parseStatementStartsWithOpenBracket(annots, true, possibleMappingField);
}
private STNode parseMemberBracketedList(boolean possibleMappingField) {
STNode annots = STNodeFactory.createEmptyNodeList();
return parseStatementStartsWithOpenBracket(annots, false, possibleMappingField);
}
/**
* 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 parseStatementStartsWithOpenBracket(STNode annots, boolean isRoot, boolean possibleMappingField) {
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(annots, openBracket, memberList, member, isRoot);
case LIST_BINDING_PATTERN:
return parseAsListBindingPattern(openBracket, memberList, member, isRoot);
case LIST_CONSTRUCTOR:
return parseAsListConstructor(openBracket, memberList, member, isRoot);
case LIST_BP_OR_LIST_CONSTRUCTOR:
return parseAsListBindingPatternOrListConstructor(openBracket, memberList, member, isRoot);
case TUPLE_TYPE_DESC_OR_LIST_CONST:
return parseAsTupleTypeDescOrListConstructor(annots, openBracket, memberList, member, isRoot);
case NONE:
default:
memberList.add(member);
break;
}
STNode memberEnd = parseBracketedListMemberEnd();
if (memberEnd == null) {
break;
}
memberList.add(memberEnd);
}
STNode closeBracket = parseCloseBracket();
STNode bracketedList = parseStatementStartBracketedList(annots, openBracket, memberList, closeBracket, isRoot,
possibleMappingField);
return bracketedList;
}
private STNode parseStatementStartBracketedListMember() {
STToken nextToken = peek();
return parseStatementStartBracketedListMember(nextToken.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 parseMemberBracketedList(false);
case IDENTIFIER_TOKEN:
STNode identifier = parseQualifiedIdentifier(ParserRuleContext.VARIABLE_REF);
if (isWildcardBP(identifier)) {
STNode varName = ((STSimpleNameReferenceNode) identifier).name;
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);
case OPEN_PAREN_TOKEN:
return parseTypeDescOrExpr();
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 parseAsTupleTypeDescOrListConstructor(STNode annots, STNode openBracket, List<STNode> memberList,
STNode member, boolean isRoot) {
memberList.add(member);
STNode memberEnd = parseBracketedListMemberEnd();
STNode tupleTypeDescOrListCons;
if (memberEnd == null) {
STNode closeBracket = parseCloseBracket();
tupleTypeDescOrListCons =
parseTupleTypeDescOrListConstructorRhs(openBracket, memberList, closeBracket, isRoot);
} else {
memberList.add(memberEnd);
tupleTypeDescOrListCons = parseTupleTypeDescOrListConstructor(annots, openBracket, memberList, isRoot);
}
return tupleTypeDescOrListCons;
}
/**
* Parse tuple type desc or list constructor.
*
* @return Parsed node
*/
private STNode parseTupleTypeDescOrListConstructor(STNode annots) {
startContext(ParserRuleContext.BRACKETED_LIST);
STNode openBracket = parseOpenBracket();
List<STNode> memberList = new ArrayList<>();
return parseTupleTypeDescOrListConstructor(annots, openBracket, memberList, false);
}
private STNode parseTupleTypeDescOrListConstructor(STNode annots, STNode openBracket, List<STNode> memberList,
boolean isRoot) {
STToken nextToken = peek();
while (!isBracketedListEnd(nextToken.kind)) {
STNode member = parseTupleTypeDescOrListConstructorMember(nextToken.kind, annots);
SyntaxKind currentNodeType = getParsingNodeTypeOfTupleTypeOrListCons(member);
switch (currentNodeType) {
case LIST_CONSTRUCTOR:
return parseAsListConstructor(openBracket, memberList, member, isRoot);
case TUPLE_TYPE_DESC:
return parseAsTupleTypeDesc(annots, openBracket, memberList, member, isRoot);
case TUPLE_TYPE_DESC_OR_LIST_CONST:
default:
memberList.add(member);
break;
}
STNode memberEnd = parseBracketedListMemberEnd();
if (memberEnd == null) {
break;
}
memberList.add(memberEnd);
nextToken = peek();
}
STNode closeBracket = parseCloseBracket();
return parseTupleTypeDescOrListConstructorRhs(openBracket, memberList, closeBracket, isRoot);
}
private STNode parseTupleTypeDescOrListConstructorMember(SyntaxKind nextTokenKind, STNode annots) {
switch (nextTokenKind) {
case OPEN_BRACKET_TOKEN:
return parseTupleTypeDescOrListConstructor(annots);
case IDENTIFIER_TOKEN:
STNode identifier = parseQualifiedIdentifier(ParserRuleContext.VARIABLE_REF);
nextTokenKind = peek().kind;
if (nextTokenKind == SyntaxKind.ELLIPSIS_TOKEN) {
STNode ellipsis = parseEllipsis();
return STNodeFactory.createRestDescriptorNode(identifier, ellipsis);
}
return parseExpressionRhs(DEFAULT_OP_PRECEDENCE, identifier, false, false);
case OPEN_BRACE_TOKEN:
return parseMappingConstructorExpr();
case ERROR_KEYWORD:
if (getNextNextToken(nextTokenKind).kind == SyntaxKind.OPEN_PAREN_TOKEN) {
return parseErrorConstructorExpr();
}
return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TUPLE);
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);
case OPEN_PAREN_TOKEN:
return parseTypeDescOrExpr();
default:
if (isValidExpressionStart(nextTokenKind, 1)) {
return parseExpression(false);
}
if (isTypeStartingToken(nextTokenKind)) {
return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TUPLE);
}
Solution solution = recover(peek(), ParserRuleContext.TUPLE_TYPE_DESC_OR_LIST_CONST_MEMBER);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseStatementStartBracketedListMember(solution.tokenKind);
}
}
private SyntaxKind getParsingNodeTypeOfTupleTypeOrListCons(STNode memberNode) {
return getStmtStartBracketedListType(memberNode);
}
private STNode parseTupleTypeDescOrListConstructorRhs(STNode openBracket, List<STNode> members, STNode closeBracket,
boolean isRoot) {
STNode tupleTypeOrListConst;
switch (peek().kind) {
case COMMA_TOKEN:
case CLOSE_BRACE_TOKEN:
case CLOSE_BRACKET_TOKEN:
if (!isRoot) {
endContext();
return new STAmbiguousCollectionNode(SyntaxKind.TUPLE_TYPE_DESC_OR_LIST_CONST, openBracket, members,
closeBracket);
}
default:
if (isValidExprRhsStart(peek().kind, closeBracket.kind) ||
(isRoot && peek().kind == SyntaxKind.EQUAL_TOKEN)) {
members = getExpressionList(members);
STNode memberExpressions = STNodeFactory.createNodeList(members);
tupleTypeOrListConst = STNodeFactory.createListConstructorExpressionNode(openBracket,
memberExpressions, closeBracket);
break;
}
STNode memberTypeDescs = STNodeFactory.createNodeList(getTypeDescList(members));
STNode tupleTypeDesc =
STNodeFactory.createTupleTypeDescriptorNode(openBracket, memberTypeDescs, closeBracket);
tupleTypeOrListConst =
parseComplexTypeDescriptor(tupleTypeDesc, ParserRuleContext.TYPE_DESC_IN_TUPLE, false);
}
endContext();
if (!isRoot) {
return tupleTypeOrListConst;
}
return parseStmtStartsWithTupleTypeOrExprRhs(null, tupleTypeOrListConst, isRoot);
}

Line under review: namespaceUri = parseSimpleConstExpr();

Code after addressing the 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.
* <p>
* <code>
* listener-decl := metadata [public] listener [type-descriptor] variable-name = expression ;
* </code>
*
* @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();
if (peek().kind == SyntaxKind.IDENTIFIER_TOKEN) {
STNode listenerDecl =
parseConstantOrListenerDeclWithOptionalType(metadata, qualifier, listenerKeyword, true);
endContext();
return listenerDecl;
}
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 keyword) {
switch (nextTokenKind) {
case ANNOTATION_KEYWORD:
switchContext(ParserRuleContext.ANNOTATION_DECL);
return parseAnnotationDeclaration(metadata, qualifier, keyword);
case IDENTIFIER_TOKEN:
return parseConstantOrListenerDeclWithOptionalType(metadata, qualifier, keyword, false);
default:
if (isTypeStartingToken(nextTokenKind)) {
break;
}
STToken token = peek();
Solution solution = recover(token, ParserRuleContext.CONST_DECL_TYPE, metadata, qualifier, keyword);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseConstDeclFromType(solution.tokenKind, metadata, qualifier, keyword);
}
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, keyword, typeDesc, variableName,
equalsToken, initializer, semicolonToken);
}
private STNode parseConstantOrListenerDeclWithOptionalType(STNode metadata, STNode qualifier, STNode constKeyword,
boolean isListener) {
STNode varNameOrTypeName = parseStatementStartIdentifier();
STNode constDecl =
parseConstantOrListenerDeclRhs(metadata, qualifier, constKeyword, varNameOrTypeName, isListener);
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 keyword Keyword
* @param typeOrVarName Identifier that follows the const-keywoord
* @return Parsed node
*/
private STNode parseConstantOrListenerDeclRhs(STNode metadata, STNode qualifier, STNode keyword,
STNode typeOrVarName, boolean isListener) {
if (typeOrVarName.kind == SyntaxKind.QUALIFIED_NAME_REFERENCE) {
STNode type = typeOrVarName;
STNode variableName = parseVariableName();
return parseListenerOrConstRhs(metadata, qualifier, keyword, isListener, type, variableName);
}
STToken token = peek();
return parseConstantOrListenerDeclRhs(token.kind, metadata, qualifier, keyword, typeOrVarName, isListener);
}
private STNode parseConstantOrListenerDeclRhs(SyntaxKind nextTokenKind, STNode metadata, STNode qualifier,
STNode keyword, STNode typeOrVarName, boolean isListener) {
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, keyword,
typeOrVarName, isListener);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseConstantOrListenerDeclRhs(solution.tokenKind, metadata, qualifier, keyword, typeOrVarName,
isListener);
}
return parseListenerOrConstRhs(metadata, qualifier, keyword, isListener, type, variableName);
}
private STNode parseListenerOrConstRhs(STNode metadata, STNode qualifier, STNode keyword, boolean isListener,
STNode type, STNode variableName) {
STNode equalsToken = parseAssignOp();
STNode initializer = parseExpression();
STNode semicolonToken = parseSemicolon();
if (isListener) {
return STNodeFactory.createListenerDeclarationNode(metadata, qualifier, keyword, type, variableName,
equalsToken, initializer, semicolonToken);
}
return STNodeFactory.createConstantDeclarationNode(metadata, qualifier, keyword, 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, boolean isInConditionalExpr) {
STNode typeofKeyword = parseTypeofKeyword();
STNode expr = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false, isInConditionalExpr);
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();
typeDescriptorNode = validateForUsageOfVar(typeDescriptorNode);
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, boolean isInConditionalExpr) {
STNode unaryOperator = parseUnaryOperator();
STNode expr = parseExpression(OperatorPrecedence.UNARY, isRhsExpr, false, isInConditionalExpr);
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 createArrayTypeDesc(memberTypeDesc, openBracketToken, arrayLengthNode, closeBracketToken);
}
private STNode createArrayTypeDesc(STNode memberTypeDesc, STNode openBracketToken, STNode arrayLengthNode,
STNode closeBracketToken) {
memberTypeDesc = validateForUsageOfVar(memberTypeDesc);
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 parseOptionalAnnotations() {
STToken nextToken = peek();
return parseOptionalAnnotations(nextToken.kind);
}
private STNode parseOptionalAnnotations(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 list with atleast one annotation.
*
* @return Annotation list
*/
private STNode parseAnnotations() {
startContext(ParserRuleContext.ANNOTATIONS);
List<STNode> annotList = new ArrayList<>();
annotList.add(parseAnnotation());
while (peek().kind == SyntaxKind.AT_TOKEN) {
annotList.add(parseAnnotation());
}
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_STRING:
docString = parseMarkdownDocumentation();
annotations = parseOptionalAnnotations();
break;
case AT_TOKEN:
docString = STNodeFactory.createEmptyNode();
annotations = parseOptionalAnnotations(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.createEmptyNodeList());
}
/**
* 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, boolean isInConditionalExpr) {
STNode isKeyword = parseIsKeyword();
STNode typeDescriptor =
parseTypeDescriptorInExpression(ParserRuleContext.TYPE_DESC_IN_EXPRESSION, isInConditionalExpr);
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);
}
/**
* Parse statement which is only consists of an action or expression.
*
* @param annots Annotations
* @param nextTokenKind Next token kind
* @return Statement node
*/
private STNode parseExpressionStatement(SyntaxKind nextTokenKind, STNode annots) {
startContext(ParserRuleContext.EXPRESSION_STATEMENT);
STNode expression = parseActionOrExpressionInLhs(nextTokenKind, annots);
return getExpressionAsStatement(expression);
}
/**
* Parse statements that starts with an expression.
*
* @return Statement node
*/
private STNode parseStatementStartWithExpr(STNode annots) {
startContext(ParserRuleContext.AMBIGUOUS_STMT);
STNode expr = parseActionOrExpressionInLhs(peek().kind, annots);
return parseStatementStartWithExprRhs(expr);
}
/**
* Parse rhs of statements that starts with an expression.
*
* @return Statement node
*/
private STNode parseStatementStartWithExprRhs(STNode expression) {
STToken nextToken = peek();
return parseStatementStartWithExprRhs(nextToken.kind, expression);
}
/**
* Parse the component followed by the expression, at the beginning of a statement.
*
* @param nextTokenKind Kind of the next token
* @return Statement node
*/
private STNode parseStatementStartWithExprRhs(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);
}
ParserRuleContext context;
if (isPossibleExpressionStatement(expression)) {
context = ParserRuleContext.EXPR_STMT_RHS;
} else {
context = ParserRuleContext.STMT_START_WITH_EXPR_RHS;
}
Solution solution = recover(peek(), context, expression);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseStatementStartWithExprRhs(solution.tokenKind, expression);
}
}
private boolean isPossibleExpressionStatement(STNode expression) {
switch (expression.kind) {
case METHOD_CALL:
case FUNCTION_CALL:
case CHECK_EXPRESSION:
case FAIL_EXPRESSION:
case REMOTE_METHOD_CALL_ACTION:
case CHECK_ACTION:
case FAIL_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 true;
default:
return false;
}
}
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 FAIL_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:
case FAIL_EXPRESSION:
return parseActionStatement(expression);
default:
STNode semicolon = parseSemicolon();
endContext();
STNode exprStmt = STNodeFactory.createExpressionStatementNode(SyntaxKind.INVALID_EXPRESSION_STATEMENT,
expression, semicolon);
exprStmt = SyntaxErrors.addDiagnostic(exprStmt, DiagnosticErrorCode.ERROR_INVALID_EXPRESSION_STATEMENT);
return exprStmt;
}
}
private STNode parseArrayTypeDescriptorNode(STIndexedExpressionNode indexedExpr) {
STNode memberTypeDesc = getTypeDescFromExpr(indexedExpr.containerExpression);
STNodeList lengthExprs = (STNodeList) indexedExpr.keyExpression;
if (lengthExprs.isEmpty()) {
return createArrayTypeDesc(memberTypeDesc, indexedExpr.openBracket, STNodeFactory.createEmptyNode(),
indexedExpr.closeBracket);
}
STNode lengthExpr = lengthExprs.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:
STNode newOpenBracketWithDiagnostics = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(
indexedExpr.openBracket, lengthExpr, DiagnosticErrorCode.ERROR_INVALID_ARRAY_LENGTH);
indexedExpr = indexedExpr.replace(indexedExpr.openBracket, newOpenBracketWithDiagnostics);
lengthExpr = STNodeFactory.createEmptyNode();
}
return createArrayTypeDesc(memberTypeDesc, indexedExpr.openBracket, lengthExpr, indexedExpr.closeBracket);
}
/**
* <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) {
STNode semicolon = parseSemicolon();
endContext();
return STNodeFactory.createExpressionStatementNode(SyntaxKind.CALL_STATEMENT, expression, semicolon);
}
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(expression, isRhsExpr, rightArrow);
}
private STNode parseRemoteCallOrAsyncSendActionRhs(STNode expression, boolean isRhsExpr, STNode rightArrow) {
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:
STNode defaultKeyword = parseDefaultKeyword();
name = STNodeFactory.createSimpleNameReferenceNode(defaultKeyword);
return parseAsyncSendAction(expression, rightArrow, name);
case IDENTIFIER_TOKEN:
name = STNodeFactory.createSimpleNameReferenceNode(parseFunctionName());
break;
case CONTINUE_KEYWORD:
case COMMIT_KEYWORD:
name = getKeywordAsSimpleNameRef();
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 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> keyword token.
*
* @return Parsed node
*/
private STNode parseParameterizedTypeKeyword() {
STToken nextToken = peek();
switch (nextToken.kind) {
case MAP_KEYWORD:
case FUTURE_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 = parseQualifiedIdentifier(ParserRuleContext.ANNOT_DECL_OPTIONAL_TYPE);
if (typeDescOrAnnotTag.kind == SyntaxKind.QUALIFIED_NAME_REFERENCE) {
STNode annotTag = parseAnnotationTag();
return parseAnnotationDeclAttachPoints(metadata, qualifier, constKeyword, annotationKeyword,
typeDescOrAnnotTag, annotTag);
}
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.IDENTIFIER_TOKEN || isValidTypeContinuationToken(nextToken)) {
STNode typeDesc = parseComplexTypeDescriptor(typeDescOrAnnotTag,
ParserRuleContext.TYPE_DESC_IN_ANNOTATION_DECL, false);
STNode annotTag = parseAnnotationTag();
return parseAnnotationDeclAttachPoints(metadata, qualifier, constKeyword, annotationKeyword, typeDesc,
annotTag);
}
STNode annotTag = ((STSimpleNameReferenceNode) typeDescOrAnnotTag).name;
return parseAnnotationDeclRhs(metadata, qualifier, constKeyword, annotationKeyword, annotTag);
}
/**
* 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.createEmptyNodeList();
break;
case ON_KEYWORD:
onKeyword = parseOnKeyword();
attachPoints = parseAnnotationAttachPoints();
onKeyword = cloneWithDiagnosticIfListEmpty(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 = SyntaxErrors.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 parseXMLNamespaceDeclaration(boolean isModuleVar) {
startContext(ParserRuleContext.XML_NAMESPACE_DECLARATION);
STNode xmlnsKeyword = parseXMLNSKeyword();
STNode namespaceUri = parseSimpleConstExpr();
while (!isValidXMLNameSpaceURI(namespaceUri)) {
xmlnsKeyword = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(xmlnsKeyword,
namespaceUri, DiagnosticErrorCode.ERROR_INVALID_XML_NAMESPACE_URI);
namespaceUri = parseSimpleConstExpr();
}
STNode xmlnsDecl = parseXMLDeclRhs(xmlnsKeyword, namespaceUri, isModuleVar);
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;
}
}
private boolean isValidXMLNameSpaceURI(STNode expr) {
switch (expr.kind) {
case STRING_LITERAL:
case QUALIFIED_NAME_REFERENCE:
case SIMPLE_NAME_REFERENCE:
return true;
case IDENTIFIER_TOKEN:
default:
return false;
}
}
private STNode parseSimpleConstExpr() {
startContext(ParserRuleContext.CONSTANT_EXPRESSION);
STNode expr = parseSimpleConstExprInternal();
endContext();
return expr;
}
private STNode parseSimpleConstExprInternal() {
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_PAREN_TOKEN:
return parseNilLiteral();
default:
STToken token = peek();
Solution solution = recover(token, ParserRuleContext.CONSTANT_EXPRESSION_START);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return STNodeFactory.createSimpleNameReferenceNode(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, boolean isModuleVar) {
return parseXMLDeclRhs(peek().kind, xmlnsKeyword, namespaceUri, isModuleVar);
}
private STNode parseXMLDeclRhs(SyntaxKind nextTokenKind, STNode xmlnsKeyword, STNode namespaceUri,
boolean isModuleVar) {
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, isModuleVar);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseXMLDeclRhs(solution.tokenKind, xmlnsKeyword, namespaceUri, isModuleVar);
}
STNode semicolon = parseSemicolon();
if (isModuleVar) {
return STNodeFactory.createModuleXMLNamespaceDeclarationNode(xmlnsKeyword, namespaceUri, asKeyword,
namespacePrefix, semicolon);
}
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 = parseOptionalAnnotations();
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 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,
boolean isTypedBindingPattern) {
STNode pipeToken = parsePipeToken();
STNode rightTypeDesc = parseTypeDescriptor(context, isTypedBindingPattern, false);
return createUnionTypeDesc(leftTypeDesc, pipeToken, rightTypeDesc);
}
private STNode createUnionTypeDesc(STNode leftTypeDesc, STNode pipeToken, STNode rightTypeDesc) {
leftTypeDesc = validateForUsageOfVar(leftTypeDesc);
rightTypeDesc = validateForUsageOfVar(rightTypeDesc);
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:
case DISTINCT_KEYWORD:
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 TYPEDESC_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 READONLY_KEYWORD:
return SyntaxKind.READONLY_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 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();
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:
if (workers.isEmpty()) {
openBrace = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(openBrace, stmt,
DiagnosticErrorCode.ERROR_ONLY_NAMED_WORKERS_ALLOWED_HERE);
} else {
updateLastNodeInListWithInvalidNode(workers, stmt,
DiagnosticErrorCode.ERROR_ONLY_NAMED_WORKERS_ALLOWED_HERE);
}
}
}
STNode namedWorkerDeclarations = STNodeFactory.createNodeList(workers);
STNode closeBrace = parseCloseBrace();
endContext();
STNode forkStmt =
STNodeFactory.createForkStatementNode(forkKeyword, openBrace, namedWorkerDeclarations, closeBrace);
if (isNodeListEmpty(namedWorkerDeclarations)) {
return SyntaxErrors.addDiagnostic(forkStmt,
DiagnosticErrorCode.ERROR_MISSING_NAMED_WORKER_DECLARATION_IN_FORK_STMT);
}
return forkStmt;
}
/**
* 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, boolean isInConditionalExpr) {
STNode trapKeyword = parseTrapKeyword();
STNode expr =
parseExpression(OperatorPrecedence.EXPRESSION_ACTION, isRhsExpr, allowActions, isInConditionalExpr);
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.createEmptyNodeList();
}
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, boolean allowActions, boolean isInConditionalExpr) {
startContext(ParserRuleContext.TYPE_CAST);
STNode ltToken = parseLTToken();
STNode typeCastParam = parseTypeCastParam();
STNode gtToken = parseGTToken();
endContext();
STNode expression =
parseExpression(OperatorPrecedence.EXPRESSION_ACTION, isRhsExpr, allowActions, isInConditionalExpr);
return STNodeFactory.createTypeCastExpressionNode(ltToken, typeCastParam, gtToken, expression);
}
private STNode parseTypeCastParam() {
STNode annot;
STNode type;
STToken token = peek();
switch (token.kind) {
case AT_TOKEN:
annot = parseOptionalAnnotations();
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() {
STToken nextToken = peek();
if (isEndOfTableRowList(nextToken.kind)) {
return STNodeFactory.createEmptyNodeList();
}
List<STNode> mappings = new ArrayList<>();
STNode mapExpr = parseMappingConstructorExpr();
mappings.add(mapExpr);
nextToken = peek();
STNode rowEnd;
while (!isEndOfTableRowList(nextToken.kind)) {
rowEnd = parseTableRowEnd(nextToken.kind);
if (rowEnd == null) {
break;
}
mappings.add(rowEnd);
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);
}
}
private STNode parseTableRowEnd() {
STNode nextToken = peek();
return parseTableRowEnd(nextToken.kind);
}
private STNode parseTableRowEnd(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_BRACKET_TOKEN:
case EOF_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.TABLE_ROW_END);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseListConstructorMemberEnd(solution.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 (isKeyKeyword(token)) {
return getKeyKeyword(consume());
} else {
Solution sol = recover(token, ParserRuleContext.KEY_KEYWORD);
return sol.recoveredNode;
}
}
static boolean isKeyKeyword(STToken token) {
return token.kind == SyntaxKind.IDENTIFIER_TOKEN && LexerTerminals.KEY.equals(token.text());
}
private STNode getKeyKeyword(STToken token) {
return STNodeFactory.createToken(SyntaxKind.KEY_KEYWORD, token.leadingMinutiae(), token.trailingMinutiae(),
token.diagnostics());
}
/**
* Parse field names.
* <p>
* <code>field-name-list := [ field-name (, field-name)* ]</code>
*
* @return Parsed node
*/
private STNode parseFieldNames() {
STToken nextToken = peek();
if (isEndOfFieldNamesList(nextToken.kind)) {
return STNodeFactory.createEmptyNodeList();
}
List<STNode> fieldNames = 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();
if (nextToken.kind == SyntaxKind.LT_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 typedesc type descriptor.
* typedesc-type-descriptor := typedesc type-parameter
*
* @return Parsed typedesc type node
*/
private STNode parseTypedescTypeDescriptor() {
STNode typedescKeywordToken = parseTypedescKeyword();
STNode typedescTypeParamsNode;
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.LT_TOKEN) {
typedescTypeParamsNode = parseTypeParameter();
} else {
typedescTypeParamsNode = STNodeFactory.createEmptyNode();
}
return STNodeFactory.createTypedescTypeDescriptorNode(typedescKeywordToken, typedescTypeParamsNode);
}
/**
* Parse typedesc-keyword.
*
* @return Parsed typedesc-keyword node
*/
private STNode parseTypedescKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.TYPEDESC_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.TYPEDESC_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 xml type descriptor.
* xml-type-descriptor := xml type-parameter
*
* @return Parsed typedesc type node
*/
private STNode parseXmlTypeDescriptor() {
STNode xmlKeywordToken = parseXMLKeyword();
STNode typedescTypeParamsNode;
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.LT_TOKEN) {
typedescTypeParamsNode = parseTypeParameter();
} else {
typedescTypeParamsNode = STNodeFactory.createEmptyNode();
}
return STNodeFactory.createXmlTypeDescriptorNode(xmlKeywordToken, typedescTypeParamsNode);
}
/**
* 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, false);
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, false);
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 = cloneWithDiagnosticIfListEmpty(letVarDeclarations, letKeyword,
DiagnosticErrorCode.ERROR_MISSING_LET_VARIABLE_DECLARATION);
STNode expression = parseExpression(OperatorPrecedence.QUERY, 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 = parseOptionalAnnotations();
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();
while (true) {
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.EOF_TOKEN || nextToken.kind == SyntaxKind.CLOSE_BRACE_TOKEN) {
break;
} else {
nextToken = consume();
expr = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(expr, nextToken,
DiagnosticErrorCode.ERROR_INVALID_TOKEN, nextToken.text());
}
}
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;
}
}
/**
* 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 (isKeyKeyword(nextToken)) {
STNode keyKeywordToken = getKeyKeyword(consume());
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.
* @param isRhsExpr Is expression in rhs context
* @return Anonymous function expression node
*/
private STNode parseExplicitFunctionExpression(STNode annots, boolean isRhsExpr) {
startContext(ParserRuleContext.ANON_FUNC_EXPRESSION);
STNode funcKeyword = parseFunctionKeyword();
STNode funcSignature = parseFuncSignature(false);
STNode funcBody = parseAnonFuncBody(isRhsExpr);
return STNodeFactory.createExplicitAnonymousFunctionExpressionNode(annots, funcKeyword, funcSignature,
funcBody);
}
/**
* Parse anonymous function body.
* <p>
* <code>anon-func-body := block-function-body | expr-function-body</code>
*
* @param isRhsExpr Is expression in rhs context
* @return Anon function body node
*/
private STNode parseAnonFuncBody(boolean isRhsExpr) {
return parseAnonFuncBody(peek().kind, isRhsExpr);
}
private STNode parseAnonFuncBody(SyntaxKind nextTokenKind, boolean isRhsExpr) {
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, isRhsExpr);
default:
Solution solution = recover(peek(), ParserRuleContext.ANON_FUNC_BODY, isRhsExpr);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseAnonFuncBody(solution.tokenKind, isRhsExpr);
}
}
/**
* Parse expression function body.
* <p>
* <code>expr-function-body := => expression</code>
*
* @param isAnon Is anonymous function.
* @param isRhsExpr Is expression in rhs context
* @return Expression function body node
*/
private STNode parseExpressionFuncBody(boolean isAnon, boolean isRhsExpr) {
STNode rightDoubleArrow = parseDoubleRightArrow();
STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
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, boolean isRhsExpr) {
switch (params.kind) {
case SIMPLE_NAME_REFERENCE:
case INFER_PARAM_LIST:
break;
case BRACED_EXPRESSION:
params = getAnonFuncParam((STBracedExpressionNode) params);
break;
default:
STToken syntheticParam = STNodeFactory.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN);
syntheticParam = SyntaxErrors.cloneWithLeadingInvalidNodeMinutiae(syntheticParam, params,
DiagnosticErrorCode.ERROR_INVALID_PARAM_LIST_IN_INFER_ANONYMOUS_FUNCTION_EXPR);
params = STNodeFactory.createSimpleNameReferenceNode(syntheticParam);
}
STNode rightDoubleArrow = parseDoubleRightArrow();
STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
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
* @param isRhsExpr Is expression in rhs context
* @return Implicit anon function expression node
*/
private STNode parseImplicitAnonFunc(STNode openParen, STNode firstParam, boolean isRhsExpr) {
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);
param = STNodeFactory.createSimpleNameReferenceNode(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, isRhsExpr);
}
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 = cloneWithDiagnosticIfListEmpty(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, false);
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, false);
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 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
*               [query-construct-type] query-pipeline select-clause on-conflict-clause? limit-lause?
* <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 = parseQueryConstructType(parseStreamKeyword(), null);
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) {
STToken nextToken = peek();
return parseTableConstructorOrQuery(nextToken.kind, nextToken, tableKeyword, isRhsExpr);
}
private STNode parseTableConstructorOrQuery(SyntaxKind nextTokenKind, STToken nextToken, 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);
case IDENTIFIER_TOKEN:
if (isKeyKeyword(nextToken)) {
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, null, 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 | stream</code>
*
* @return Parsed node
*/
private STNode parseQueryConstructType(STNode keyword, STNode keySpecifier) {
return STNodeFactory.createQueryConstructTypeNode(keyword, keySpecifier);
}
/**
* Parse query expression.
* <p>
* <code>
* query-expr-rhs := query-pipeline select-clause
*                   query-pipeline select-clause on-conflict-clause? limit-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, SyntaxKind.NONE)) {
intermediateClause = parseIntermediateClause(isRhsExpr);
if (intermediateClause == null) {
break;
}
if (selectClause != null) {
selectClause = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(selectClause, intermediateClause,
DiagnosticErrorCode.ERROR_MORE_CLAUSES_AFTER_SELECT_CLAUSE);
continue;
}
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, isRhsExpr);
}
if (selectClause == null) {
STNode selectKeyword = SyntaxErrors.createMissingToken(SyntaxKind.SELECT_KEYWORD);
STNode expr = STNodeFactory
.createSimpleNameReferenceNode(SyntaxErrors.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN));
selectClause = STNodeFactory.createSelectClauseNode(selectKeyword, expr);
if (clauses.isEmpty()) {
fromClause = SyntaxErrors.addDiagnostic(fromClause, DiagnosticErrorCode.ERROR_MISSING_SELECT_CLAUSE);
} else {
int lastIndex = clauses.size() - 1;
STNode intClauseWithDiagnostic = SyntaxErrors.addDiagnostic(clauses.get(lastIndex),
DiagnosticErrorCode.ERROR_MISSING_SELECT_CLAUSE);
clauses.set(lastIndex, intClauseWithDiagnostic);
}
}
STNode intermediateClauses = STNodeFactory.createNodeList(clauses);
STNode queryPipeline = STNodeFactory.createQueryPipelineNode(fromClause, intermediateClauses);
STNode onConflictClause = parseOnConflictClause(isRhsExpr);
STNode limitClause = parseLimitClause(isRhsExpr);
return STNodeFactory.createQueryExpressionNode(queryConstructType, queryPipeline, selectClause,
onConflictClause, limitClause);
}
/**
* Parse limit keyword.
*
* @return Limit keyword node
*/
private STNode parseLimitKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.LIMIT_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.LIMIT_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse an intermediate clause.
* <p>
* <code>
* intermediate-clause := from-clause | where-clause | let-clause
* </code>
*
* @return Parsed node
*/
private STNode parseIntermediateClause(boolean isRhsExpr) {
STToken nextToken = peek();
return parseIntermediateClause(nextToken.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 JOIN_KEYWORD:
case OUTER_KEYWORD:
return parseJoinClause(isRhsExpr);
case ORDER_KEYWORD:
case BY_KEYWORD:
case ASCENDING_KEYWORD:
case DESCENDING_KEYWORD:
return parseOrderByClause(isRhsExpr);
case DO_KEYWORD:
case SEMICOLON_TOKEN:
case ON_KEYWORD:
case CONFLICT_KEYWORD:
case LIMIT_KEYWORD:
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);
}
}
/**
* Parse select-keyword.
*
* @return Select-keyword node
*/
private STNode parseJoinKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.JOIN_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.JOIN_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse select-keyword.
*
* @return Select-keyword node
*/
private STNode parseOuterKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.OUTER_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.OUTER_KEYWORD);
return sol.recoveredNode;
}
}
private boolean isEndOfIntermediateClause(SyntaxKind tokenKind, SyntaxKind precedingNodeKind) {
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_STRING:
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, precedingNodeKind);
}
}
/**
* 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 = cloneWithDiagnosticIfListEmpty(letVarDeclarations, letKeyword,
DiagnosticErrorCode.ERROR_MISSING_LET_VARIABLE_DECLARATION);
return STNodeFactory.createLetClauseNode(letKeyword, letVarDeclarations);
}
/**
* Parse order-keyword.
*
* @return Order-keyword node
*/
private STNode parseOrderKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.ORDER_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.ORDER_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse by-keyword.
*
* @return By-keyword node
*/
private STNode parseByKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.BY_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.BY_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse ascending-keyword.
*
* @return Ascending-keyword node
*/
private STNode parseAscendingKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.ASCENDING_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.ASCENDING_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse descending-keyword.
*
* @return Descending-keyword node
*/
private STNode parseDescendingKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.DESCENDING_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.DESCENDING_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse order by clause.
* <p>
* <code>order-by-clause := order by order-key-list
* </code>
*
* @return Parsed node
*/
private STNode parseOrderByClause(boolean isRhsExpr) {
STNode orderKeyword = parseOrderKeyword();
STNode byKeyword = parseByKeyword();
STNode orderKeys = parseOrderKeyList(isRhsExpr);
byKeyword = cloneWithDiagnosticIfListEmpty(orderKeys, byKeyword,
DiagnosticErrorCode.ERROR_MISSING_ORDER_KEY);
return STNodeFactory.createOrderByClauseNode(orderKeyword, byKeyword, orderKeys);
}
/**
* Parse order key.
* <p>
* <code>order-key-list := order-key [, order-key]*</code>
*
* @return Parsed node
*/
private STNode parseOrderKeyList(boolean isRhsExpr) {
startContext(ParserRuleContext.ORDER_KEY);
List<STNode> orderKeys = new ArrayList<>();
STToken nextToken = peek();
if (isEndOfOrderKeys(nextToken.kind)) {
endContext();
return STNodeFactory.createEmptyNodeList();
}
STNode orderKey = parseOrderKey(isRhsExpr);
orderKeys.add(orderKey);
nextToken = peek();
STNode orderKeyListMemberEnd;
while (!isEndOfOrderKeys(nextToken.kind)) {
orderKeyListMemberEnd = parseOrderKeyListMemberEnd();
if (orderKeyListMemberEnd == null) {
break;
}
orderKeys.add(orderKeyListMemberEnd);
orderKey = parseOrderKey(isRhsExpr);
orderKeys.add(orderKey);
nextToken = peek();
}
endContext();
return STNodeFactory.createNodeList(orderKeys);
}
private boolean isEndOfOrderKeys(SyntaxKind tokenKind) {
switch (tokenKind) {
case COMMA_TOKEN:
return false;
case SEMICOLON_TOKEN:
case EOF_TOKEN:
return true;
default:
return isNextQueryClauseStart(tokenKind);
}
}
private boolean isNextQueryClauseStart(SyntaxKind tokenKind) {
switch(tokenKind) {
case SELECT_KEYWORD:
case LET_KEYWORD:
case WHERE_KEYWORD:
return true;
default:
return false;
}
}
private STNode parseOrderKeyListMemberEnd() {
return parseOrderKeyListMemberEnd(peek().kind);
}
private STNode parseOrderKeyListMemberEnd(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case SELECT_KEYWORD:
case WHERE_KEYWORD:
case LET_KEYWORD:
case EOF_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.ORDER_KEY_LIST_END);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseOrderKeyListMemberEnd(solution.tokenKind);
}
}
/**
* Parse order key.
* <p>
* <code>order-key := expression (ascending | descending)?</code>
*
* @return Parsed node
*/
private STNode parseOrderKey(boolean isRhsExpr) {
STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
STToken nextToken = peek();
switch (nextToken.kind) {
case ASCENDING_KEYWORD:
STNode ascendingKeyword = parseAscendingKeyword();
return STNodeFactory.createOrderKeyNode(expression, ascendingKeyword);
case DESCENDING_KEYWORD:
STNode descendingKeyword = parseDescendingKeyword();
return STNodeFactory.createOrderKeyNode(expression, descendingKeyword);
default:
return STNodeFactory.createOrderKeyNode(expression, STNodeFactory.createEmptyNode());
}
}
/**
* 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 on-conflict clause.
* <p>
* <code>
* onConflictClause := on conflict expression
* </code>
*
* @return On conflict clause node
*/
private STNode parseOnConflictClause(boolean isRhsExpr) {
STToken nextToken = peek();
if (nextToken.kind != SyntaxKind.ON_KEYWORD) {
return STNodeFactory.createEmptyNode();
}
STNode onKeyword = parseOnKeyword();
STNode conflictKeyword = parseConflictKeyword();
STNode expr = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
return STNodeFactory.createOnConflictClauseNode(onKeyword, conflictKeyword, expr);
}
/**
* Parse conflict keyword.
*
* @return Conflict keyword node
*/
private STNode parseConflictKeyword() {
STToken token = peek();
if (token.kind == SyntaxKind.CONFLICT_KEYWORD) {
return consume();
} else {
Solution sol = recover(token, ParserRuleContext.CONFLICT_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse limit clause.
* <p>
* <code>limitClause := limit expression</code>
*
* @return Limit expression node
*/
private STNode parseLimitClause(boolean isRhsExpr) {
STToken nextToken = peek();
if (nextToken.kind != SyntaxKind.LIMIT_KEYWORD) {
return STNodeFactory.createEmptyNode();
}
STNode limitKeyword = parseLimitKeyword();
STNode expr = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
return STNodeFactory.createLimitClauseNode(limitKeyword, expr);
}
/**
* Parse join clause.
* <p>
* <code>
* join-clause := (join-var-decl | outer-join-var-decl) in expression
* <br/>
* join-var-decl := join (typeName | var) bindingPattern
* <br/>
* outer-join-var-decl := outer join var binding-pattern
* </code>
*
* @return Join clause
*/
private STNode parseJoinClause(boolean isRhsExpr) {
STNode outerKeyword;
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.OUTER_KEYWORD) {
outerKeyword = parseOuterKeyword();
} else {
outerKeyword = STNodeFactory.createEmptyNode();
}
STNode joinKeyword = parseJoinKeyword();
STNode typedBindingPattern = parseTypedBindingPattern(ParserRuleContext.JOIN_CLAUSE);
STNode inKeyword = parseInKeyword();
STNode onCondition;
STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
nextToken = peek();
if (nextToken.kind == SyntaxKind.ON_KEYWORD) {
onCondition = parseOnClause(isRhsExpr);
} else {
onCondition = STNodeFactory.createEmptyNode();
}
return STNodeFactory.createJoinClauseNode(outerKeyword, joinKeyword, typedBindingPattern, inKeyword, expression,
onCondition);
}
/**
* Parse on clause.
* <p>
* <code>on clause := on expression</code>
*
* @return On clause node
*/
private STNode parseOnClause(boolean isRhsExpr) {
STNode onKeyword = parseOnKeyword();
STNode expression = parseExpression(OperatorPrecedence.QUERY, isRhsExpr, false);
return STNodeFactory.createOnClauseNode(onKeyword, expression);
}
/**
* 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();
switch (expr.kind) {
case FUNCTION_CALL:
case METHOD_CALL:
case REMOTE_METHOD_CALL_ACTION:
break;
case SIMPLE_NAME_REFERENCE:
case QUALIFIED_NAME_REFERENCE:
STNode openParenToken = SyntaxErrors.createMissingTokenWithDiagnostics(SyntaxKind.OPEN_PAREN_TOKEN,
DiagnosticErrorCode.ERROR_MISSING_OPEN_PAREN_TOKEN);
STNode arguments = STNodeFactory.createEmptyNodeList();
STNode closeParenToken = SyntaxErrors.createMissingTokenWithDiagnostics(SyntaxKind.CLOSE_PAREN_TOKEN,
DiagnosticErrorCode.ERROR_MISSING_CLOSE_PAREN_TOKEN);
expr = STNodeFactory.createFunctionCallExpressionNode(expr, openParenToken, arguments, closeParenToken);
break;
default:
startKeyword = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(startKeyword, expr,
DiagnosticErrorCode.ERROR_INVALID_EXPRESSION_IN_START_ACTION);
STNode funcName = SyntaxErrors.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN);
funcName = STNodeFactory.createSimpleNameReferenceNode(funcName);
openParenToken = SyntaxErrors.createMissingToken(SyntaxKind.OPEN_PAREN_TOKEN);
arguments = STNodeFactory.createEmptyNodeList();
closeParenToken = SyntaxErrors.createMissingToken(SyntaxKind.CLOSE_PAREN_TOKEN);
expr = STNodeFactory.createFunctionCallExpressionNode(funcName, openParenToken, arguments,
closeParenToken);
break;
}
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;
}
}
/**
* 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,
boolean isTypedBindingPattern) {
STNode bitwiseAndToken = consume();
STNode rightTypeDesc = parseTypeDescriptor(context, isTypedBindingPattern, false);
return createIntersectionTypeDesc(leftTypeDesc, bitwiseAndToken, rightTypeDesc);
}
private STNode createIntersectionTypeDesc(STNode leftTypeDesc, STNode bitwiseAndToken, STNode rightTypeDesc) {
leftTypeDesc = validateForUsageOfVar(leftTypeDesc);
rightTypeDesc = validateForUsageOfVar(rightTypeDesc);
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 = parseSimpleConstExpr();
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 = parseBasicLiteral();
break;
default:
literal = parseDecimalIntLiteral(ParserRuleContext.DECIMAL_INTEGER_LITERAL);
literal = STNodeFactory.createBasicLiteralNode(literal.kind, 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) {
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:
SyntaxKind nextNextTokenKind = peek(nextTokenIndex).kind;
return nextNextTokenKind == SyntaxKind.SEMICOLON_TOKEN || nextNextTokenKind == SyntaxKind.COMMA_TOKEN ||
nextNextTokenKind == SyntaxKind.CLOSE_BRACKET_TOKEN ||
isValidExprRhsStart(nextNextTokenKind, SyntaxKind.SIMPLE_NAME_REFERENCE);
case IDENTIFIER_TOKEN:
return isValidExprRhsStart(peek(nextTokenIndex).kind, SyntaxKind.SIMPLE_NAME_REFERENCE);
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 FROM_KEYWORD:
case LET_KEYWORD:
case BACKTICK_TOKEN:
case NEW_KEYWORD:
case FAIL_KEYWORD:
case LEFT_ARROW_TOKEN:
return true;
case PLUS_TOKEN:
case MINUS_TOKEN:
return isValidExpressionStart(peek(nextTokenIndex).kind, nextTokenIndex);
case FUNCTION_KEYWORD:
case TABLE_KEYWORD:
return peek(nextTokenIndex).kind == SyntaxKind.FROM_KEYWORD;
case STREAM_KEYWORD:
STToken nextNextToken = peek(nextTokenIndex);
return nextNextToken.kind == SyntaxKind.KEY_KEYWORD ||
nextNextToken.kind == SyntaxKind.OPEN_BRACKET_TOKEN ||
nextNextToken.kind == SyntaxKind.FROM_KEYWORD;
case ERROR_KEYWORD:
return peek(nextTokenIndex).kind == SyntaxKind.OPEN_PAREN_TOKEN;
case SERVICE_KEYWORD:
return peek(nextTokenIndex).kind == SyntaxKind.OPEN_BRACE_TOKEN;
case XML_KEYWORD:
case STRING_KEYWORD:
return peek(nextTokenIndex).kind == SyntaxKind.BACKTICK_TOKEN;
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);
if (sol.action == Action.REMOVE) {
return sol.recoveredNode;
}
return STNodeFactory.createSimpleNameReferenceNode(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();
openBrace = cloneWithDiagnosticIfListEmpty(receiveFields, openBrace,
DiagnosticErrorCode.ERROR_MISSING_RECEIVE_FIELD_IN_RECEIVE_ACTION);
return STNodeFactory.createReceiveFieldsNode(openBrace, receiveFields, closeBrace);
}
private STNode parseReceiveFields() {
List<STNode> receiveFields = new ArrayList<>();
STToken nextToken = peek();
if (isEndOfReceiveFields(nextToken.kind)) {
return STNodeFactory.createEmptyNodeList();
}
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_BRACE_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:
STNode defaultKeyword = parseDefaultKeyword();
return STNodeFactory.createSimpleNameReferenceNode(defaultKeyword);
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 = consume();
STToken endLGToken = consume();
STNode doubleGTToken = STNodeFactory.createToken(SyntaxKind.DOUBLE_GT_TOKEN, openGTToken.leadingMinutiae(),
endLGToken.trailingMinutiae());
if (!validateRightShiftOperatorWS(openGTToken)) {
doubleGTToken = SyntaxErrors.addDiagnostic(doubleGTToken,
DiagnosticErrorCode.ERROR_NO_WHITESPACES_ALLOWED_IN_RIGHT_SHIFT_OP);
}
return doubleGTToken;
}
/**
* Parse unsigned right shift token (>>>).
*
* @return Parsed node
*/
private STNode parseUnsignedRightShiftToken() {
STNode openGTToken = consume();
STNode middleGTToken = consume();
STNode endLGToken = consume();
STNode unsignedRightShiftToken = STNodeFactory.createToken(SyntaxKind.TRIPPLE_GT_TOKEN,
openGTToken.leadingMinutiae(), endLGToken.trailingMinutiae());
boolean validOpenGTToken = validateRightShiftOperatorWS(openGTToken);
boolean validMiddleGTToken = validateRightShiftOperatorWS(middleGTToken);
if (validOpenGTToken && validMiddleGTToken) {
return unsignedRightShiftToken;
}
unsignedRightShiftToken = SyntaxErrors.addDiagnostic(unsignedRightShiftToken,
DiagnosticErrorCode.ERROR_NO_WHITESPACES_ALLOWED_IN_UNSIGNED_RIGHT_SHIFT_OP);
return unsignedRightShiftToken;
}
/**
* Validate the whitespace between '>' tokens of right shift operators.
*
* @param node Preceding node
* @return the validated node
*/
private boolean validateRightShiftOperatorWS(STNode node) {
int diff = node.widthWithTrailingMinutiae() - node.width();
return diff == 0;
}
/**
* 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);
STToken nextToken = peek();
if (isEndOfWaitFutureExprList(nextToken.kind)) {
endContext();
STNode waitFutureExprs = STNodeFactory
.createSimpleNameReferenceNode(STNodeFactory.createMissingToken(SyntaxKind.IDENTIFIER_TOKEN));
waitFutureExprs = SyntaxErrors.addDiagnostic(waitFutureExprs,
DiagnosticErrorCode.ERROR_MISSING_WAIT_FUTURE_EXPRESSION);
return STNodeFactory.createWaitActionNode(waitKeyword, waitFutureExprs);
}
List<STNode> waitFutureExprList = new ArrayList<>();
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 = parseActionOrExpression();
if (waitFutureExpr.kind == SyntaxKind.MAPPING_CONSTRUCTOR) {
waitFutureExpr = SyntaxErrors.addDiagnostic(waitFutureExpr,
DiagnosticErrorCode.ERROR_MAPPING_CONSTRUCTOR_EXPR_AS_A_WAIT_EXPR);
} else if (isAction(waitFutureExpr)) {
waitFutureExpr =
SyntaxErrors.addDiagnostic(waitFutureExpr, DiagnosticErrorCode.ERROR_ACTION_AS_A_WAIT_EXPR);
}
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();
openBrace = cloneWithDiagnosticIfListEmpty(waitFields, openBrace,
DiagnosticErrorCode.ERROR_MISSING_WAIT_FIELD_IN_WAIT_ACTION);
STNode waitFieldsNode = STNodeFactory.createWaitFieldsListNode(openBrace, waitFields, closeBrace);
return STNodeFactory.createWaitActionNode(waitKeyword, waitFieldsNode);
}
private STNode parseWaitFields() {
List<STNode> waitFields = new ArrayList<>();
STToken nextToken = peek();
if (isEndOfWaitFields(nextToken.kind)) {
return STNodeFactory.createEmptyNodeList();
}
STNode waitField = parseWaitField();
waitFields.add(waitField);
nextToken = peek();
STNode waitFieldEnd;
while (!isEndOfWaitFields(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 boolean isEndOfWaitFields(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case EOF_TOKEN:
case CLOSE_BRACE_TOKEN:
return true;
default:
return false;
}
}
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);
identifier = STNodeFactory.createSimpleNameReferenceNode(identifier);
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, boolean isInConditionalExpr) {
STNode annotAccessToken = parseAnnotChainingToken();
STNode annotTagReference = parseFieldAccessIdentifier(isInConditionalExpr);
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(boolean isInConditionalExpr) {
return parseQualifiedIdentifier(ParserRuleContext.FIELD_ACCESS_IDENTIFIER, isInConditionalExpr);
}
/**
* 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, boolean isRhsExpr) {
if (selectClause != null) {
queryPipeline = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(queryPipeline, selectClause,
DiagnosticErrorCode.ERROR_SELECT_CLAUSE_IN_QUERY_ACTION);
}
startContext(ParserRuleContext.DO_CLAUSE);
STNode doKeyword = parseDoKeyword();
STNode blockStmt = parseBlockNode();
endContext();
STNode limitClause = parseLimitClause(isRhsExpr);
return STNodeFactory.createQueryActionNode(queryPipeline, doKeyword, blockStmt, limitClause);
}
/**
* 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, boolean isInConditionalExpr) {
STNode optionalFieldAccessToken = parseOptionalChainingToken();
STNode fieldName = parseFieldAccessIdentifier(isInConditionalExpr);
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.ANON_FUNC_OR_LET, true, false, true);
STNode nextToken = peek();
STNode endExpr;
STNode colon;
if (nextToken.kind != SyntaxKind.COLON_TOKEN && middleExpr.kind == SyntaxKind.QUALIFIED_NAME_REFERENCE) {
STQualifiedNameReferenceNode qualifiedNameRef = (STQualifiedNameReferenceNode) middleExpr;
middleExpr = STNodeFactory.createSimpleNameReferenceNode(qualifiedNameRef.modulePrefix);
colon = qualifiedNameRef.colon;
endContext();
endExpr = STNodeFactory.createSimpleNameReferenceNode(qualifiedNameRef.identifier);
} else {
colon = parseColon();
endContext();
endExpr = parseExpression(OperatorPrecedence.ANON_FUNC_OR_LET, 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();
openBraceToken = cloneWithDiagnosticIfListEmpty(enumMemberList, openBraceToken,
DiagnosticErrorCode.ERROR_MISSING_ENUM_MEMBER);
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);
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.CLOSE_BRACE_TOKEN) {
return STNodeFactory.createEmptyNodeList();
}
List<STNode> enumMemberList = 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_STRING:
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);
if (startingBackTick.isMissing()) {
startingBackTick = SyntaxErrors.createMissingToken(SyntaxKind.BACKTICK_TOKEN);
STNode endingBackTick = SyntaxErrors.createMissingToken(SyntaxKind.BACKTICK_TOKEN);
STNode content = STNodeFactory.createEmptyNode();
STNode byteArrayLiteral =
STNodeFactory.createByteArrayLiteralNode(type, startingBackTick, content, endingBackTick);
byteArrayLiteral =
SyntaxErrors.addDiagnostic(byteArrayLiteral, DiagnosticErrorCode.ERROR_MISSING_BYTE_ARRAY_CONTENT);
return byteArrayLiteral;
}
STNode content = parseByteArrayContent(kind);
return parseByteArrayLiteral(kind, type, startingBackTick, content);
}
/**
* Parse byte array literal.
*
* @param baseKind indicates the SyntaxKind base16 or base64
* @param typeKeyword keyword token, possible values are `base16` and `base64`
* @param startingBackTick starting backtick token
* @param byteArrayContent byte array literal content to be validated
* @return parsed byte array literal node
*/
private STNode parseByteArrayLiteral(SyntaxKind baseKind, STNode typeKeyword, STNode startingBackTick,
STNode byteArrayContent) {
STNode content = STNodeFactory.createEmptyNode();
STNode newStartingBackTick = startingBackTick;
STNodeList items = (STNodeList) byteArrayContent;
if (items.size() == 1) {
STNode item = items.get(0);
if (baseKind == SyntaxKind.BASE16_KEYWORD && !isValidBase16LiteralContent(item.toString())) {
newStartingBackTick = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(startingBackTick, item,
DiagnosticErrorCode.ERROR_INVALID_BASE16_CONTENT_IN_BYTE_ARRAY_LITERAL);
} else if (baseKind == SyntaxKind.BASE64_KEYWORD && !isValidBase64LiteralContent(item.toString())) {
newStartingBackTick = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(startingBackTick, item,
DiagnosticErrorCode.ERROR_INVALID_BASE64_CONTENT_IN_BYTE_ARRAY_LITERAL);
} else if (item.kind != SyntaxKind.TEMPLATE_STRING) {
newStartingBackTick = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(startingBackTick, item,
DiagnosticErrorCode.ERROR_INVALID_CONTENT_IN_BYTE_ARRAY_LITERAL);
} else {
content = item;
}
} else if (items.size() > 1) {
STNode clonedStartingBackTick = startingBackTick;
for (int index = 0; index < items.size(); index++) {
STNode item = items.get(index);
clonedStartingBackTick =
SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(clonedStartingBackTick, item);
}
newStartingBackTick = SyntaxErrors.addDiagnostic(clonedStartingBackTick,
DiagnosticErrorCode.ERROR_INVALID_CONTENT_IN_BYTE_ARRAY_LITERAL);
}
STNode endingBackTick = parseBacktickToken(ParserRuleContext.TEMPLATE_END);
return STNodeFactory.createByteArrayLiteralNode(typeKeyword, newStartingBackTick, 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) {
STToken nextToken = peek();
List<STNode> items = new ArrayList<>();
while (!isEndOfBacktickContent(nextToken.kind)) {
STNode content = parseTemplateItem();
items.add(content);
nextToken = peek();
}
return STNodeFactory.createNodeList(items);
}
/**
* Validate base16 literal content.
* <p>
* <code>
* Base16Literal := base16 WS ` HexGroup* WS `
* <br/>
* HexGroup := WS HexDigit WS HexDigit
* <br/>
* WS := WhiteSpaceChar*
* <br/>
* WhiteSpaceChar := 0x9 | 0xA | 0xD | 0x20
* </code>
*
* @param content the string surrounded by the backticks
* @return <code>true</code>, if the string content is valid. <code>false</code> otherwise.
*/
static boolean isValidBase16LiteralContent(String content) {
char[] charArray = content.toCharArray();
int hexDigitCount = 0;
for (char c : charArray) {
switch (c) {
case LexerTerminals.TAB:
case LexerTerminals.NEWLINE:
case LexerTerminals.CARRIAGE_RETURN:
case LexerTerminals.SPACE:
break;
default:
if (isHexDigit(c)) {
hexDigitCount++;
} else {
return false;
}
break;
}
}
return hexDigitCount % 2 == 0;
}
/**
* Validate base64 literal content.
* <p>
* <code>
* Base64Literal := base64 WS ` Base64Group* [PaddedBase64Group] WS `
* <br/>
* Base64Group := WS Base64Char WS Base64Char WS Base64Char WS Base64Char
* <br/>
* PaddedBase64Group :=
*    WS Base64Char WS Base64Char WS Base64Char WS PaddingChar
*    | WS Base64Char WS Base64Char WS PaddingChar WS PaddingChar
* <br/>
* Base64Char := A .. Z | a .. z | 0 .. 9 | + | /
* <br/>
* PaddingChar := =
* <br/>
* WS := WhiteSpaceChar*
* <br/>
* WhiteSpaceChar := 0x9 | 0xA | 0xD | 0x20
* </code>
*
* @param content the string surrounded by the backticks
* @return <code>true</code>, if the string content is valid. <code>false</code> otherwise.
*/
static boolean isValidBase64LiteralContent(String content) {
char[] charArray = content.toCharArray();
int base64CharCount = 0;
int paddingCharCount = 0;
for (char c : charArray) {
switch (c) {
case LexerTerminals.TAB:
case LexerTerminals.NEWLINE:
case LexerTerminals.CARRIAGE_RETURN:
case LexerTerminals.SPACE:
break;
case LexerTerminals.EQUAL:
paddingCharCount++;
break;
default:
if (isBase64Char(c)) {
if (paddingCharCount == 0) {
base64CharCount++;
} else {
return false;
}
} else {
return false;
}
break;
}
}
if (paddingCharCount > 2) {
return false;
} else if (paddingCharCount == 0) {
return base64CharCount % 4 == 0;
} else {
return base64CharCount % 4 == 4 - paddingCharCount;
}
}
/**
* <p>
* Check whether a given char is a base64 char.
* </p>
* <code>Base64Char := A .. Z | a .. z | 0 .. 9 | + | /</code>
*
* @param c character to check
* @return <code>true</code>, if the character represents a base64 char. <code>false</code> otherwise.
*/
static boolean isBase64Char(int c) {
if ('a' <= c && c <= 'z') {
return true;
}
if ('A' <= c && c <= 'Z') {
return true;
}
if (c == '+' || c == '/') {
return true;
}
return isDigit(c);
}
static boolean isHexDigit(int c) {
if ('a' <= c && c <= 'f') {
return true;
}
if ('A' <= c && c <= 'F') {
return true;
}
return isDigit(c);
}
static boolean isDigit(int c) {
return ('0' <= c && c <= '9');
}
/**
* 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();
startToken = cloneWithDiagnosticIfListEmpty(xmlNamePattern, startToken,
DiagnosticErrorCode.ERROR_MISSING_XML_ATOMIC_NAME_PATTERN);
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)) {
return STNodeFactory.createNodeList(xmlAtomicNamePatternList);
}
STNode xmlAtomicNamePattern = parseXMLAtomicNamePattern();
xmlAtomicNamePatternList.add(xmlAtomicNamePattern);
STNode separator;
while (!isEndOfXMLNamePattern(peek().kind)) {
separator = parseXMLNamePatternSeparator();
if (separator == null) {
break;
}
xmlAtomicNamePatternList.add(separator);
xmlAtomicNamePattern = parseXMLAtomicNamePattern();
xmlAtomicNamePatternList.add(xmlAtomicNamePattern);
}
return STNodeFactory.createNodeList(xmlAtomicNamePatternList);
}
private boolean isEndOfXMLNamePattern(SyntaxKind tokenKind) {
switch (tokenKind) {
case GT_TOKEN:
case EOF_TOKEN:
return true;
case IDENTIFIER_TOKEN:
case ASTERISK_TOKEN:
case COLON_TOKEN:
default:
return false;
}
}
private STNode parseXMLNamePatternSeparator() {
STToken token = peek();
switch (token.kind) {
case PIPE_TOKEN:
return consume();
case GT_TOKEN:
case EOF_TOKEN:
return null;
default:
Solution sol = recover(token, ParserRuleContext.XML_NAME_PATTERN_RHS);
if (sol.tokenKind == SyntaxKind.GT_TOKEN) {
return null;
}
return sol.recoveredNode;
}
}
/**
* 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;
}
}
/**
* Parse match statement.
* <p>
* <code>match-stmt := match action-or-expr { match-clause+ }</code>
*
* @return Match statement
*/
private STNode parseMatchStatement() {
startContext(ParserRuleContext.MATCH_STMT);
STNode matchKeyword = parseMatchKeyword();
STNode actionOrExpr = parseActionOrExpression();
startContext(ParserRuleContext.MATCH_BODY);
STNode openBrace = parseOpenBrace();
STNode matchClauses = parseMatchClauses();
STNode closeBrace = parseCloseBrace();
endContext();
endContext();
return STNodeFactory.createMatchStatementNode(matchKeyword, actionOrExpr, openBrace, matchClauses, closeBrace);
}
/**
* Parse match keyword.
*
* @return Match keyword node
*/
private STNode parseMatchKeyword() {
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.MATCH_KEYWORD) {
return consume();
} else {
Solution sol = recover(nextToken, ParserRuleContext.MATCH_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse match clauses list.
*
* @return Match clauses list
*/
private STNode parseMatchClauses() {
List<STNode> matchClauses = new ArrayList<>();
while (!isEndOfMatchClauses(peek().kind)) {
STNode clause = parseMatchClause();
matchClauses.add(clause);
}
return STNodeFactory.createNodeList(matchClauses);
}
private boolean isEndOfMatchClauses(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case EOF_TOKEN:
case CLOSE_BRACE_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse a single match match clause.
* <p>
* <code>
* match-clause := match-pattern-list [match-guard] => block-stmt
* <br/>
* match-guard := if expression
* </code>
*
* @return A match clause
*/
private STNode parseMatchClause() {
STNode matchPatterns = parseMatchPatternList();
STNode matchGuard = parseMatchGuard();
STNode rightDoubleArrow = parseDoubleRightArrow();
STNode blockStmt = parseBlockNode();
return STNodeFactory.createMatchClauseNode(matchPatterns, matchGuard, rightDoubleArrow, blockStmt);
}
/**
* Parse match guard.
* <p>
* <code>match-guard := if expression</code>
*
* @return Match guard
*/
private STNode parseMatchGuard() {
STToken nextToken = peek();
return parseMatchGuard(nextToken.kind);
}
private STNode parseMatchGuard(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case IF_KEYWORD:
STNode ifKeyword = parseIfKeyword();
STNode expr = parseExpression(peek().kind, DEFAULT_OP_PRECEDENCE, true, false, true, false);
return STNodeFactory.createMatchGuardNode(ifKeyword, expr);
case RIGHT_DOUBLE_ARROW_TOKEN:
return STNodeFactory.createEmptyNode();
default:
Solution solution = recover(peek(), ParserRuleContext.OPTIONAL_MATCH_GUARD);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseMatchGuard(solution.tokenKind);
}
}
/**
* Parse match patterns list.
* <p>
* <code>match-pattern-list := match-pattern (| match-pattern)*</code>
*
* @return Match patterns list
*/
private STNode parseMatchPatternList() {
startContext(ParserRuleContext.MATCH_PATTERN);
List<STNode> matchClauses = new ArrayList<>();
while (!isEndOfMatchPattern(peek().kind)) {
STNode clause = parseMatchPattern();
if (clause == null) {
break;
}
matchClauses.add(clause);
STNode seperator = parseMatchPatternEnd();
if (seperator == null) {
break;
}
matchClauses.add(seperator);
}
endContext();
return STNodeFactory.createNodeList(matchClauses);
}
private boolean isEndOfMatchPattern(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case PIPE_TOKEN:
case IF_KEYWORD:
case RIGHT_ARROW_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse match pattern.
* <p>
* <code>
* match-pattern := var binding-pattern
*                  | wildcard-match-pattern
*                  | const-pattern
*                  | list-match-pattern
*                  | mapping-match-pattern
*                  | functional-match-pattern
* </code>
*
* @return Match pattern
*/
private STNode parseMatchPattern() {
STToken nextToken = peek();
return parseMatchPattern(nextToken.kind);
}
private STNode parseMatchPattern(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case OPEN_PAREN_TOKEN:
case NULL_KEYWORD:
case TRUE_KEYWORD:
case FALSE_KEYWORD:
case PLUS_TOKEN:
case MINUS_TOKEN:
case DECIMAL_INTEGER_LITERAL:
case HEX_INTEGER_LITERAL:
case DECIMAL_FLOATING_POINT_LITERAL:
case HEX_FLOATING_POINT_LITERAL:
case STRING_LITERAL:
return parseSimpleConstExpr();
case IDENTIFIER_TOKEN:
STNode typeRefOrConstExpr = parseQualifiedIdentifier(ParserRuleContext.MATCH_PATTERN);
return parseFunctionalMatchPatternOrConsPattern(typeRefOrConstExpr);
case VAR_KEYWORD:
return parseVarTypedBindingPattern();
case OPEN_BRACKET_TOKEN:
return parseListMatchPattern();
case OPEN_BRACE_TOKEN:
return parseMappingMatchPattern();
case ERROR_KEYWORD:
return parseFunctionalMatchPattern(consume());
default:
Solution solution = recover(peek(), ParserRuleContext.MATCH_PATTERN_START);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseMatchPattern(solution.tokenKind);
}
}
private STNode parseMatchPatternEnd() {
STToken nextToken = peek();
return parseMatchPatternEnd(nextToken.kind);
}
private STNode parseMatchPatternEnd(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case PIPE_TOKEN:
return parsePipeToken();
case IF_KEYWORD:
case RIGHT_DOUBLE_ARROW_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.MATCH_PATTERN_RHS);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseMatchPatternEnd(solution.tokenKind);
}
}
/**
* Parse var typed binding pattern.
* <p>
* <code>var binding-pattern</code>
* </p>
*
* @return Parsed typed binding pattern node
*/
private STNode parseVarTypedBindingPattern() {
STNode varKeyword = parseVarKeyword();
STNode bindingPattern = parseBindingPattern();
return STNodeFactory.createTypedBindingPatternNode(varKeyword, bindingPattern);
}
/**
* Parse var keyword.
*
* @return Var keyword node
*/
private STNode parseVarKeyword() {
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.VAR_KEYWORD) {
return consume();
} else {
Solution sol = recover(nextToken, ParserRuleContext.VAR_KEYWORD);
return sol.recoveredNode;
}
}
/**
* Parse list match pattern.
* <p>
* <code>
*         list-match-pattern := [ list-member-match-patterns ]
*         list-member-match-patterns :=
*          match-pattern (, match-pattern)* [, rest-match-pattern]
*          | [ rest-match-pattern ]
*     </code>
* </p>
*
* @return Parsed list match pattern node
*/
private STNode parseListMatchPattern() {
startContext(ParserRuleContext.LIST_MATCH_PATTERN);
STNode openBracketToken = parseOpenBracket();
List<STNode> matchPatternList = new ArrayList<>();
STNode restMatchPattern = null;
while (!isEndOfListMatchPattern()) {
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.ELLIPSIS_TOKEN) {
restMatchPattern = parseRestMatchPattern();
break;
}
STNode matchPatternListMember = parseMatchPattern();
matchPatternList.add(matchPatternListMember);
STNode matchPatternMemberRhs = parseListMatchPatternMemberRhs();
if (matchPatternMemberRhs != null) {
matchPatternList.add(matchPatternMemberRhs);
} else {
break;
}
}
if (restMatchPattern == null) {
restMatchPattern = STNodeFactory.createEmptyNode();
}
STNode matchPatternListNode = STNodeFactory.createNodeList(matchPatternList);
STNode closeBracketToken = parseCloseBracket();
endContext();
return STNodeFactory.createListMatchPatternNode(openBracketToken, matchPatternListNode, restMatchPattern,
closeBracketToken);
}
public boolean isEndOfListMatchPattern() {
switch (peek().kind) {
case CLOSE_BRACKET_TOKEN:
case EOF_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse rest match pattern.
* <p>
* <code>
*         rest-match-pattern := ... var variable-name
*     </code>
* </p>
*
* @return Parsed rest match pattern node
*/
private STNode parseRestMatchPattern() {
startContext(ParserRuleContext.REST_MATCH_PATTERN);
STNode ellipsisToken = parseEllipsis();
STNode varKeywordToken = parseVarKeyword();
STNode variableName = parseVariableName();
endContext();
STSimpleNameReferenceNode simpleNameReferenceNode =
(STSimpleNameReferenceNode) STNodeFactory.createSimpleNameReferenceNode(variableName);
return STNodeFactory.createRestMatchPatternNode(ellipsisToken, varKeywordToken, simpleNameReferenceNode);
}
private STNode parseListMatchPatternMemberRhs() {
return parseListMatchPatternMemberRhs(peek().kind);
}
private STNode parseListMatchPatternMemberRhs(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_BRACKET_TOKEN:
case EOF_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.LIST_MATCH_PATTERN_MEMBER_RHS);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseListMatchPatternMemberRhs(solution.tokenKind);
}
}
/**
* Parse mapping match pattern.
* <p>
* mapping-match-pattern := { field-match-patterns }
* <br/>
* field-match-patterns := field-match-pattern (, field-match-pattern)* [, rest-match-pattern]
* | [ rest-match-pattern ]
* <br/>
* field-match-pattern := field-name : match-pattern
* <br/>
* rest-match-pattern := ... var variable-name
* </p>
*
* @return Parsed Node.
*/
private STNode parseMappingMatchPattern() {
startContext(ParserRuleContext.MAPPING_MATCH_PATTERN);
STNode openBraceToken = parseOpenBrace();
List<STNode> fieldMatchPatternList = new ArrayList<>();
STNode restMatchPattern = null;
boolean isEndOfFields = false;
while (!isEndOfMappingMatchPattern()) {
STNode fieldMatchPatternMember = parseFieldMatchPatternMember();
if (fieldMatchPatternMember.kind == SyntaxKind.REST_MATCH_PATTERN) {
restMatchPattern = fieldMatchPatternMember;
isEndOfFields = true;
break;
}
fieldMatchPatternList.add(fieldMatchPatternMember);
STNode fieldMatchPatternRhs = parseFieldMatchPatternRhs();
if (fieldMatchPatternRhs != null) {
fieldMatchPatternList.add(fieldMatchPatternRhs);
} else {
break;
}
}
STNode fieldMatchPatternRhs = parseFieldMatchPatternRhs();
while (isEndOfFields && fieldMatchPatternRhs != null) {
STNode invalidField = parseFieldMatchPatternMember();
restMatchPattern =
SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(restMatchPattern, fieldMatchPatternRhs);
restMatchPattern = SyntaxErrors.cloneWithTrailingInvalidNodeMinutiae(restMatchPattern, invalidField);
restMatchPattern = SyntaxErrors.addDiagnostic(restMatchPattern,
DiagnosticErrorCode.ERROR_MORE_FIELD_MATCH_PATTERNS_AFTER_REST_FIELD);
fieldMatchPatternRhs = parseFieldMatchPatternRhs();
}
if (restMatchPattern == null) {
restMatchPattern = STNodeFactory.createEmptyNode();
}
STNode fieldMatchPatterns = STNodeFactory.createNodeList(fieldMatchPatternList);
STNode closeBraceToken = parseCloseBrace();
endContext();
return STNodeFactory.createMappingMatchPatternNode(openBraceToken, fieldMatchPatterns, restMatchPattern,
closeBraceToken);
}
private STNode parseFieldMatchPatternMember() {
return parseFieldMatchPatternMember(peek().kind);
}
private STNode parseFieldMatchPatternMember(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case IDENTIFIER_TOKEN:
return parseFieldMatchPattern();
case ELLIPSIS_TOKEN:
return parseRestMatchPattern();
default:
Solution solution = recover(peek(), ParserRuleContext.FIELD_MATCH_PATTERN_MEMBER);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseFieldMatchPatternMember(solution.tokenKind);
}
}
/**
* Parse filed match pattern.
* <p>
* field-match-pattern := field-name : match-pattern
* </p>
*
* @return Parsed field match pattern node
*/
public STNode parseFieldMatchPattern() {
STNode fieldNameNode = parseVariableName();
STNode colonToken = parseColon();
STNode matchPattern = parseMatchPattern();
return STNodeFactory.createFieldMatchPatternNode(fieldNameNode, colonToken, matchPattern);
}
public boolean isEndOfMappingMatchPattern() {
switch (peek().kind) {
case CLOSE_BRACE_TOKEN:
case EOF_TOKEN:
return true;
default:
return false;
}
}
private STNode parseFieldMatchPatternRhs() {
return parseFieldMatchPatternRhs(peek().kind);
}
private STNode parseFieldMatchPatternRhs(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_BRACE_TOKEN:
case EOF_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.FIELD_MATCH_PATTERN_MEMBER_RHS);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseFieldMatchPatternRhs(solution.tokenKind);
}
}
private STNode parseFunctionalMatchPatternOrConsPattern(STNode typeRefOrConstExpr) {
return parseFunctionalMatchPatternOrConsPattern(peek().kind, typeRefOrConstExpr);
}
private STNode parseFunctionalMatchPatternOrConsPattern(SyntaxKind nextToken, STNode typeRefOrConstExpr) {
switch (nextToken) {
case OPEN_PAREN_TOKEN:
return parseFunctionalMatchPattern(typeRefOrConstExpr);
default:
if (isMatchPatternEnd(peek().kind)) {
return typeRefOrConstExpr;
}
Solution solution =
recover(peek(), ParserRuleContext.FUNC_MATCH_PATTERN_OR_CONST_PATTERN, typeRefOrConstExpr);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseFunctionalMatchPatternOrConsPattern(solution.tokenKind, typeRefOrConstExpr);
}
}
private boolean isMatchPatternEnd(SyntaxKind tokenKind) {
switch (tokenKind) {
case RIGHT_DOUBLE_ARROW_TOKEN:
case COMMA_TOKEN:
case CLOSE_BRACE_TOKEN:
case CLOSE_BRACKET_TOKEN:
case CLOSE_PAREN_TOKEN:
case PIPE_TOKEN:
case IF_KEYWORD:
case EOF_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse functional match pattern.
* <p>
* functional-match-pattern := functionally-constructible-type-reference ( arg-list-match-pattern )
* <br/>
* functionally-constructible-type-reference := error | type-reference
* <br/>
* type-reference := identifier | qualified-identifier
* <br/>
* arg-list-match-pattern := positional-arg-match-patterns [, other-arg-match-patterns]
* | other-arg-match-patterns
* </p>
*
* @return Parsed functional match pattern node.
*/
private STNode parseFunctionalMatchPattern(STNode typeRef) {
startContext(ParserRuleContext.FUNCTIONAL_MATCH_PATTERN);
STNode openParenthesisToken = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
STNode argListMatchPatternNode = parseArgListMatchPatterns();
STNode closeParenthesisToken = parseCloseParenthesis();
endContext();
return STNodeFactory.createFunctionalMatchPatternNode(typeRef, openParenthesisToken, argListMatchPatternNode,
closeParenthesisToken);
}
private STNode parseArgListMatchPatterns() {
List<STNode> argListMatchPatterns = new ArrayList<>();
SyntaxKind lastValidArgKind = SyntaxKind.IDENTIFIER_TOKEN;
while (!isEndOfFunctionalMatchPattern()) {
STNode currentArg = parseArgMatchPattern();
DiagnosticErrorCode errorCode = validateArgMatchPatternOrder(lastValidArgKind, currentArg.kind);
if (errorCode == null) {
argListMatchPatterns.add(currentArg);
lastValidArgKind = currentArg.kind;
} else {
updateLastNodeInListWithInvalidNode(argListMatchPatterns, currentArg, errorCode);
}
STNode argRhs = parseArgMatchPatternRhs();
if (argRhs == null) {
break;
}
if (errorCode == null) {
argListMatchPatterns.add(argRhs);
} else {
updateLastNodeInListWithInvalidNode(argListMatchPatterns, argRhs, null);
}
}
return STNodeFactory.createNodeList(argListMatchPatterns);
}
private boolean isEndOfFunctionalMatchPattern() {
switch (peek().kind) {
case CLOSE_PAREN_TOKEN:
case EOF_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse arg match patterns.
* <code>
*     arg-match-pattern := match-pattern |   named-arg-match-pattern | rest-match-pattern
* </code>
* <br/>
* <br/>
*
* @return parsed arg match pattern node.
*/
private STNode parseArgMatchPattern() {
return parseArgMatchPattern(peek().kind);
}
private STNode parseArgMatchPattern(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case IDENTIFIER_TOKEN:
return parseNamedOrPositionalArgMatchPattern();
case ELLIPSIS_TOKEN:
return parseRestMatchPattern();
case OPEN_PAREN_TOKEN:
case NULL_KEYWORD:
case TRUE_KEYWORD:
case FALSE_KEYWORD:
case PLUS_TOKEN:
case MINUS_TOKEN:
case DECIMAL_INTEGER_LITERAL:
case HEX_INTEGER_LITERAL:
case DECIMAL_FLOATING_POINT_LITERAL:
case HEX_FLOATING_POINT_LITERAL:
case STRING_LITERAL:
case VAR_KEYWORD:
case OPEN_BRACKET_TOKEN:
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
return parseMatchPattern();
default:
Solution solution = recover(peek(), ParserRuleContext.ARG_MATCH_PATTERN);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseArgMatchPattern(solution.tokenKind);
}
}
private STNode parseNamedOrPositionalArgMatchPattern() {
STNode identifier = parseIdentifier(ParserRuleContext.MATCH_PATTERN_START);
STToken secondToken = peek();
switch (secondToken.kind) {
case EQUAL_TOKEN:
return parseNamedArgMatchPattern(identifier);
case OPEN_PAREN_TOKEN:
return parseFunctionalMatchPattern(identifier);
case COMMA_TOKEN:
case CLOSE_PAREN_TOKEN:
default:
return identifier;
}
}
/**
* Parses the next named arg match pattern.
* <br/>
* <code>named-arg-match-pattern := arg-name = match-pattern</code>
* <br/>
* <br/>
*
* @return arg match pattern list node added the new arg match pattern
*/
private STNode parseNamedArgMatchPattern(STNode identifier) {
startContext(ParserRuleContext.NAMED_ARG_MATCH_PATTERN);
STNode equalToken = parseAssignOp();
STNode matchPattern = parseMatchPattern();
endContext();
return STNodeFactory.createNamedArgMatchPatternNode(identifier, equalToken, matchPattern);
}
private STNode parseArgMatchPatternRhs() {
return parseArgMatchPatternRhs(peek().kind);
}
private STNode parseArgMatchPatternRhs(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_PAREN_TOKEN:
case EOF_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.ARG_MATCH_PATTERN_RHS);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseArgMatchPatternRhs(solution.tokenKind);
}
}
private DiagnosticErrorCode validateArgMatchPatternOrder(SyntaxKind prevArgKind, SyntaxKind currentArgKind) {
DiagnosticErrorCode errorCode = null;
switch (prevArgKind) {
case NAMED_ARG_MATCH_PATTERN:
if (currentArgKind != SyntaxKind.NAMED_ARG_MATCH_PATTERN &&
currentArgKind != SyntaxKind.REST_MATCH_PATTERN) {
errorCode = DiagnosticErrorCode.ERROR_NAMED_ARG_FOLLOWED_BY_POSITIONAL_ARG;
}
break;
case REST_MATCH_PATTERN:
errorCode = DiagnosticErrorCode.ERROR_ARG_FOLLOWED_BY_REST_ARG;
break;
default:
break;
}
return errorCode;
}
/**
* Parse markdown documentation.
*
* @return markdown documentation node
*/
private STNode parseMarkdownDocumentation() {
List<STNode> markdownDocLineList = new ArrayList<>();
STToken nextToken = peek();
while (nextToken.kind == SyntaxKind.DOCUMENTATION_STRING) {
STToken documentationString = consume();
STNode parsedDocLines = parseDocumentationString(documentationString);
appendParsedDocumentationLines(markdownDocLineList, parsedDocLines);
nextToken = peek();
}
STNode markdownDocLines = STNodeFactory.createNodeList(markdownDocLineList);
return STNodeFactory.createMarkdownDocumentationNode(markdownDocLines);
}
/**
* Parse documentation string.
*
* @return markdown documentation line list node
*/
private STNode parseDocumentationString(STToken documentationStringToken) {
List<STNode> leadingTriviaList = getLeadingTriviaList(documentationStringToken.leadingMinutiae());
TextDocument textDocument = TextDocuments.from(documentationStringToken.text());
DocumentationLexer documentationLexer =
new DocumentationLexer(textDocument.getCharacterReader(), leadingTriviaList);
AbstractTokenReader tokenReader = new TokenReader(documentationLexer);
DocumentationParser documentationParser = new DocumentationParser(tokenReader);
return documentationParser.parse();
}
private List<STNode> getLeadingTriviaList(STNode leadingMinutiaeNode) {
List<STNode> leadingTriviaList = new ArrayList<>();
int bucketCount = leadingMinutiaeNode.bucketCount();
for (int i = 0; i < bucketCount; i++) {
leadingTriviaList.add(leadingMinutiaeNode.childInBucket(i));
}
return leadingTriviaList;
}
private void appendParsedDocumentationLines(List<STNode> markdownDocLineList, STNode parsedDocLines) {
int bucketCount = parsedDocLines.bucketCount();
for (int i = 0; i < bucketCount; i++) {
STNode markdownDocLine = parsedDocLines.childInBucket(i);
markdownDocLineList.add(markdownDocLine);
}
}
/**
* Parse any statement that starts with a token that has ambiguity between being
* a type-desc or an expression.
*
* @param annots Annotations
* @return Statement node
*/
private STNode parseStmtStartsWithTypeOrExpr(SyntaxKind nextTokenKind, STNode annots) {
startContext(ParserRuleContext.AMBIGUOUS_STMT);
STNode typeOrExpr = parseTypedBindingPatternOrExpr(nextTokenKind, true);
return parseStmtStartsWithTypedBPOrExprRhs(annots, typeOrExpr);
}
private STNode parseStmtStartsWithTypedBPOrExprRhs(STNode annots, STNode typedBindingPatternOrExpr) {
if (typedBindingPatternOrExpr.kind == SyntaxKind.TYPED_BINDING_PATTERN) {
STNode finalKeyword = STNodeFactory.createEmptyNode();
switchContext(ParserRuleContext.VAR_DECL_STMT);
return parseVarDeclRhs(annots, finalKeyword, typedBindingPatternOrExpr, false);
}
STNode expr = getExpression(typedBindingPatternOrExpr);
expr = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, expr, false, true);
return parseStatementStartWithExprRhs(expr);
}
private STNode parseTypedBindingPatternOrExpr(boolean allowAssignment) {
STToken nextToken = peek();
return parseTypedBindingPatternOrExpr(nextToken.kind, allowAssignment);
}
private STNode parseTypedBindingPatternOrExpr(SyntaxKind nextTokenKind, boolean allowAssignment) {
STNode typeOrExpr;
switch (nextTokenKind) {
case OPEN_PAREN_TOKEN:
return parseTypedBPOrExprStartsWithOpenParenthesis();
case FUNCTION_KEYWORD:
return parseAnonFuncExprOrTypedBPWithFuncType();
case IDENTIFIER_TOKEN:
typeOrExpr = parseQualifiedIdentifier(ParserRuleContext.TYPE_NAME_OR_VAR_NAME);
return parseTypedBindingPatternOrExprRhs(typeOrExpr, allowAssignment);
case OPEN_BRACKET_TOKEN:
typeOrExpr = parseTypedDescOrExprStartsWithOpenBracket();
return parseTypedBindingPatternOrExprRhs(typeOrExpr, allowAssignment);
case NIL_LITERAL:
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:
STNode basicLiteral = parseBasicLiteral();
return parseTypedBindingPatternOrExprRhs(basicLiteral, allowAssignment);
default:
if (isValidExpressionStart(nextTokenKind, 1)) {
return parseActionOrExpressionInLhs(nextTokenKind, null);
}
return parseTypedBindingPattern(ParserRuleContext.VAR_DECL_STMT);
}
}
/**
* Parse the component after the ambiguous starting node. Ambiguous node could be either an expr
* or a type-desc. The component followed by this ambiguous node could be the binding-pattern or
* the expression-rhs.
*
* @param typeOrExpr Type desc or the expression
* @param allowAssignment Flag indicating whether to allow assignment. i.e.: whether this is a
*            valid lvalue expression
* @return Typed-binding-pattern node or an expression node
*/
private STNode parseTypedBindingPatternOrExprRhs(STNode typeOrExpr, boolean allowAssignment) {
STToken nextToken = peek();
return parseTypedBindingPatternOrExprRhs(nextToken.kind, typeOrExpr, allowAssignment);
}
private STNode parseTypedBindingPatternOrExprRhs(SyntaxKind nextTokenKind, STNode typeOrExpr,
boolean allowAssignment) {
switch (nextTokenKind) {
case PIPE_TOKEN:
STToken nextNextToken = peek(2);
if (nextNextToken.kind == SyntaxKind.EQUAL_TOKEN) {
return typeOrExpr;
}
STNode pipe = parsePipeToken();
STNode rhsTypedBPOrExpr = parseTypedBindingPatternOrExpr(allowAssignment);
if (rhsTypedBPOrExpr.kind == SyntaxKind.TYPED_BINDING_PATTERN) {
STTypedBindingPatternNode typedBP = (STTypedBindingPatternNode) rhsTypedBPOrExpr;
typeOrExpr = getTypeDescFromExpr(typeOrExpr);
STNode newTypeDesc = createUnionTypeDesc(typeOrExpr, pipe, typedBP.typeDescriptor);
return STNodeFactory.createTypedBindingPatternNode(newTypeDesc, typedBP.bindingPattern);
}
return STNodeFactory.createBinaryExpressionNode(SyntaxKind.BINARY_EXPRESSION, typeOrExpr, pipe,
rhsTypedBPOrExpr);
case BITWISE_AND_TOKEN:
nextNextToken = peek(2);
if (nextNextToken.kind == SyntaxKind.EQUAL_TOKEN) {
return typeOrExpr;
}
STNode ampersand = parseBinaryOperator();
rhsTypedBPOrExpr = parseTypedBindingPatternOrExpr(allowAssignment);
if (rhsTypedBPOrExpr.kind == SyntaxKind.TYPED_BINDING_PATTERN) {
STTypedBindingPatternNode typedBP = (STTypedBindingPatternNode) rhsTypedBPOrExpr;
typeOrExpr = getTypeDescFromExpr(typeOrExpr);
STNode newTypeDesc = createIntersectionTypeDesc(typeOrExpr, ampersand, typedBP.typeDescriptor);
return STNodeFactory.createTypedBindingPatternNode(newTypeDesc, typedBP.bindingPattern);
}
return STNodeFactory.createBinaryExpressionNode(SyntaxKind.BINARY_EXPRESSION, typeOrExpr, ampersand,
rhsTypedBPOrExpr);
case SEMICOLON_TOKEN:
if (isDefiniteExpr(typeOrExpr.kind)) {
return typeOrExpr;
}
if (isDefiniteTypeDesc(typeOrExpr.kind) || !isAllBasicLiterals(typeOrExpr)) {
STNode typeDesc = getTypeDescFromExpr(typeOrExpr);
return parseTypeBindingPatternStartsWithAmbiguousNode(typeDesc);
}
return typeOrExpr;
case IDENTIFIER_TOKEN:
case QUESTION_MARK_TOKEN:
if (isAmbiguous(typeOrExpr) || isDefiniteTypeDesc(typeOrExpr.kind)) {
STNode typeDesc = getTypeDescFromExpr(typeOrExpr);
return parseTypeBindingPatternStartsWithAmbiguousNode(typeDesc);
}
return typeOrExpr;
case EQUAL_TOKEN:
return typeOrExpr;
case OPEN_BRACKET_TOKEN:
return parseTypedBindingPatternOrMemberAccess(typeOrExpr, false, allowAssignment,
ParserRuleContext.AMBIGUOUS_STMT);
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
STNode typeDesc = getTypeDescFromExpr(typeOrExpr);
return parseTypeBindingPatternStartsWithAmbiguousNode(typeDesc);
default:
if (isCompoundBinaryOperator(nextTokenKind)) {
return typeOrExpr;
}
if (isValidExprRhsStart(nextTokenKind, typeOrExpr.kind)) {
return typeOrExpr;
}
STToken token = peek();
Solution solution =
recover(token, ParserRuleContext.BINDING_PATTERN_OR_EXPR_RHS, typeOrExpr, allowAssignment);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseTypedBindingPatternOrExprRhs(solution.tokenKind, typeOrExpr, allowAssignment);
}
}
private STNode parseTypeBindingPatternStartsWithAmbiguousNode(STNode typeDesc) {
startContext(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
typeDesc = parseComplexTypeDescriptor(typeDesc, ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN, false);
endContext();
return parseTypedBindingPatternTypeRhs(typeDesc, ParserRuleContext.VAR_DECL_STMT);
}
private STNode parseTypedBPOrExprStartsWithOpenParenthesis() {
STNode exprOrTypeDesc = parseTypedDescOrExprStartsWithOpenParenthesis();
if (isDefiniteTypeDesc(exprOrTypeDesc.kind)) {
return parseTypeBindingPatternStartsWithAmbiguousNode(exprOrTypeDesc);
}
return parseTypedBindingPatternOrExprRhs(exprOrTypeDesc, false);
}
private boolean isDefiniteTypeDesc(SyntaxKind kind) {
return kind.compareTo(SyntaxKind.TYPE_DESC) >= 0 && kind.compareTo(SyntaxKind.SINGLETON_TYPE_DESC) <= 0;
}
private boolean isDefiniteExpr(SyntaxKind kind) {
if (kind == SyntaxKind.QUALIFIED_NAME_REFERENCE || kind == SyntaxKind.SIMPLE_NAME_REFERENCE) {
return false;
}
return kind.compareTo(SyntaxKind.BINARY_EXPRESSION) >= 0 && kind.compareTo(SyntaxKind.FAIL_EXPRESSION) <= 0;
}
/**
* Parse type or expression that starts with open parenthesis. Possible options are:
* 1) () - nil type-desc or nil-literal
* 2) (T) - Parenthesized type-desc
* 3) (expr) - Parenthesized expression
* 4) (param, param, ..) - Anon function params
*
* @return Type-desc or expression node
*/
private STNode parseTypedDescOrExprStartsWithOpenParenthesis() {
STNode openParen = parseOpenParenthesis(ParserRuleContext.OPEN_PARENTHESIS);
STToken nextToken = peek();
if (nextToken.kind == SyntaxKind.CLOSE_PAREN_TOKEN) {
STNode closeParen = parseCloseParenthesis();
return parseTypeOrExprStartWithEmptyParenthesis(openParen, closeParen);
}
STNode typeOrExpr = parseTypeDescOrExpr();
if (isAction(typeOrExpr)) {
STNode closeParen = parseCloseParenthesis();
return STNodeFactory.createBracedExpressionNode(SyntaxKind.BRACED_ACTION, openParen, typeOrExpr,
closeParen);
}
if (isExpression(typeOrExpr.kind)) {
startContext(ParserRuleContext.BRACED_EXPR_OR_ANON_FUNC_PARAMS);
return parseBracedExprOrAnonFuncParamRhs(peek().kind, openParen, typeOrExpr, false);
}
STNode closeParen = parseCloseParenthesis();
return STNodeFactory.createParenthesisedTypeDescriptorNode(openParen, typeOrExpr, closeParen);
}
/**
* Parse type-desc or expression. This method does not handle binding patterns.
*
* @return Type-desc node or expression node
*/
private STNode parseTypeDescOrExpr() {
STToken nextToken = peek();
STNode typeOrExpr;
switch (nextToken.kind) {
case OPEN_PAREN_TOKEN:
typeOrExpr = parseTypedDescOrExprStartsWithOpenParenthesis();
break;
case FUNCTION_KEYWORD:
typeOrExpr = parseAnonFuncExprOrFuncTypeDesc();
break;
case IDENTIFIER_TOKEN:
typeOrExpr = parseQualifiedIdentifier(ParserRuleContext.TYPE_NAME_OR_VAR_NAME);
return parseTypeDescOrExprRhs(typeOrExpr);
case OPEN_BRACKET_TOKEN:
typeOrExpr = parseTypedDescOrExprStartsWithOpenBracket();
break;
case NIL_LITERAL:
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:
STNode basicLiteral = parseBasicLiteral();
return parseTypeDescOrExprRhs(basicLiteral);
default:
if (isValidExpressionStart(nextToken.kind, 1)) {
return parseActionOrExpressionInLhs(nextToken.kind, null);
}
return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
}
if (isDefiniteTypeDesc(typeOrExpr.kind)) {
return parseComplexTypeDescriptor(typeOrExpr, ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN, true);
}
return parseTypeDescOrExprRhs(typeOrExpr);
}
private boolean isExpression(SyntaxKind kind) {
switch (kind) {
case BASIC_LITERAL:
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:
return true;
default:
return kind.compareTo(SyntaxKind.BINARY_EXPRESSION) >= 0 &&
kind.compareTo(SyntaxKind.FAIL_EXPRESSION) <= 0;
}
}
/**
* Parse statement that starts with an empty parenthesis. Empty parenthesis can be
* 1) Nil literal
* 2) Nil type-desc
* 3) Anon-function params
*
* @param openParen Open parenthesis
* @param closeParen Close parenthesis
* @return Parsed node
*/
private STNode parseTypeOrExprStartWithEmptyParenthesis(STNode openParen, STNode closeParen) {
STToken nextToken = peek();
switch (nextToken.kind) {
case RIGHT_DOUBLE_ARROW_TOKEN:
STNode params = STNodeFactory.createNodeList();
STNode anonFuncParam =
STNodeFactory.createImplicitAnonymousFunctionParameters(openParen, params, closeParen);
endContext();
return anonFuncParam;
default:
return STNodeFactory.createNilLiteralNode(openParen, closeParen);
}
}
private STNode parseAnonFuncExprOrTypedBPWithFuncType() {
STNode exprOrTypeDesc = parseAnonFuncExprOrFuncTypeDesc();
if (isAction(exprOrTypeDesc) || isExpression(exprOrTypeDesc.kind)) {
return exprOrTypeDesc;
}
return parseTypedBindingPatternTypeRhs(exprOrTypeDesc, ParserRuleContext.VAR_DECL_STMT);
}
/**
* Parse anon-func-expr or function-type-desc, by resolving the ambiguity.
*
* @return Anon-func-expr or function-type-desc
*/
private STNode parseAnonFuncExprOrFuncTypeDesc() {
startContext(ParserRuleContext.FUNC_TYPE_DESC_OR_ANON_FUNC);
STNode functionKeyword = parseFunctionKeyword();
STNode funcSignature = parseFuncSignature(true);
endContext();
switch (peek().kind) {
case OPEN_BRACE_TOKEN:
case RIGHT_DOUBLE_ARROW_TOKEN:
switchContext(ParserRuleContext.EXPRESSION_STATEMENT);
startContext(ParserRuleContext.ANON_FUNC_EXPRESSION);
funcSignature = validateAndGetFuncParams((STFunctionSignatureNode) funcSignature);
STNode funcBody = parseAnonFuncBody(false);
STNode annots = STNodeFactory.createEmptyNodeList();
STNode anonFunc = STNodeFactory.createExplicitAnonymousFunctionExpressionNode(annots, functionKeyword,
funcSignature, funcBody);
return parseExpressionRhs(DEFAULT_OP_PRECEDENCE, anonFunc, false, true);
case IDENTIFIER_TOKEN:
default:
switchContext(ParserRuleContext.VAR_DECL_STMT);
STNode funcTypeDesc = STNodeFactory.createFunctionTypeDescriptorNode(functionKeyword, funcSignature);
return parseComplexTypeDescriptor(funcTypeDesc, ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN,
true);
}
}
private STNode parseTypeDescOrExprRhs(STNode typeOrExpr) {
SyntaxKind nextTokenKind = peek().kind;
return parseTypeDescOrExprRhs(nextTokenKind, typeOrExpr);
}
private STNode parseTypeDescOrExprRhs(SyntaxKind nextTokenKind, STNode typeOrExpr) {
STNode typeDesc;
switch (nextTokenKind) {
case PIPE_TOKEN:
STToken nextNextToken = peek(2);
if (nextNextToken.kind == SyntaxKind.EQUAL_TOKEN) {
return typeOrExpr;
}
STNode pipe = parsePipeToken();
STNode rhsTypeDescOrExpr = parseTypeDescOrExpr();
if (isExpression(rhsTypeDescOrExpr.kind)) {
return STNodeFactory.createBinaryExpressionNode(SyntaxKind.BINARY_EXPRESSION, typeOrExpr, pipe,
rhsTypeDescOrExpr);
}
typeDesc = getTypeDescFromExpr(typeOrExpr);
rhsTypeDescOrExpr = getTypeDescFromExpr(rhsTypeDescOrExpr);
return createUnionTypeDesc(typeDesc, pipe, rhsTypeDescOrExpr);
case BITWISE_AND_TOKEN:
nextNextToken = peek(2);
if (nextNextToken.kind != SyntaxKind.EQUAL_TOKEN) {
return typeOrExpr;
}
STNode ampersand = parseBinaryOperator();
rhsTypeDescOrExpr = parseTypeDescOrExpr();
if (isExpression(rhsTypeDescOrExpr.kind)) {
return STNodeFactory.createBinaryExpressionNode(SyntaxKind.BINARY_EXPRESSION, typeOrExpr, ampersand,
rhsTypeDescOrExpr);
}
typeDesc = getTypeDescFromExpr(typeOrExpr);
rhsTypeDescOrExpr = getTypeDescFromExpr(rhsTypeDescOrExpr);
return createIntersectionTypeDesc(typeDesc, ampersand, rhsTypeDescOrExpr);
case IDENTIFIER_TOKEN:
case QUESTION_MARK_TOKEN:
startContext(ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN);
typeDesc = parseComplexTypeDescriptor(typeOrExpr, ParserRuleContext.TYPE_DESC_IN_TYPE_BINDING_PATTERN,
false);
endContext();
return typeDesc;
case SEMICOLON_TOKEN:
return getTypeDescFromExpr(typeOrExpr);
case EQUAL_TOKEN:
case CLOSE_PAREN_TOKEN:
case CLOSE_BRACE_TOKEN:
case CLOSE_BRACKET_TOKEN:
case EOF_TOKEN:
case COMMA_TOKEN:
return typeOrExpr;
case OPEN_BRACKET_TOKEN:
return parseTypedBindingPatternOrMemberAccess(typeOrExpr, false, true,
ParserRuleContext.AMBIGUOUS_STMT);
case ELLIPSIS_TOKEN:
STNode ellipsis = parseEllipsis();
typeOrExpr = getTypeDescFromExpr(typeOrExpr);
return STNodeFactory.createRestDescriptorNode(typeOrExpr, ellipsis);
default:
if (isCompoundBinaryOperator(nextTokenKind)) {
return typeOrExpr;
}
if (isValidExprRhsStart(nextTokenKind, typeOrExpr.kind)) {
return parseExpressionRhs(nextTokenKind, DEFAULT_OP_PRECEDENCE, typeOrExpr, false, false, false,
false);
}
STToken token = peek();
Solution solution = recover(token, ParserRuleContext.BINDING_PATTERN_OR_EXPR_RHS, typeOrExpr);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseTypeDescOrExprRhs(solution.tokenKind, typeOrExpr);
}
}
private boolean isAmbiguous(STNode node) {
switch (node.kind) {
case SIMPLE_NAME_REFERENCE:
case QUALIFIED_NAME_REFERENCE:
case NIL_LITERAL:
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 BRACKETED_LIST:
return true;
case BINARY_EXPRESSION:
STBinaryExpressionNode binaryExpr = (STBinaryExpressionNode) node;
if (binaryExpr.operator.kind != SyntaxKind.PIPE_TOKEN ||
binaryExpr.operator.kind == SyntaxKind.BITWISE_AND_TOKEN) {
return false;
}
return isAmbiguous(binaryExpr.lhsExpr) && isAmbiguous(binaryExpr.rhsExpr);
case BRACED_EXPRESSION:
return isAmbiguous(((STBracedExpressionNode) node).expression);
case INDEXED_EXPRESSION:
STIndexedExpressionNode indexExpr = (STIndexedExpressionNode) node;
if (!isAmbiguous(indexExpr.containerExpression)) {
return false;
}
STNode keys = indexExpr.keyExpression;
for (int i = 0; i < keys.bucketCount(); i++) {
STNode item = keys.childInBucket(i);
if (item.kind == SyntaxKind.COMMA_TOKEN) {
continue;
}
if (!isAmbiguous(item)) {
return false;
}
}
return true;
default:
return false;
}
}
private boolean isAllBasicLiterals(STNode node) {
switch (node.kind) {
case NIL_LITERAL:
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:
return true;
case BINARY_EXPRESSION:
STBinaryExpressionNode binaryExpr = (STBinaryExpressionNode) node;
if (binaryExpr.operator.kind != SyntaxKind.PIPE_TOKEN ||
binaryExpr.operator.kind == SyntaxKind.BITWISE_AND_TOKEN) {
return false;
}
return isAmbiguous(binaryExpr.lhsExpr) && isAmbiguous(binaryExpr.rhsExpr);
case BRACED_EXPRESSION:
return isAmbiguous(((STBracedExpressionNode) node).expression);
case BRACKETED_LIST:
STAmbiguousCollectionNode list = (STAmbiguousCollectionNode) node;
for (STNode member : list.members) {
if (member.kind == SyntaxKind.COMMA_TOKEN) {
continue;
}
if (!isAllBasicLiterals(member)) {
return false;
}
}
return true;
case UNARY_EXPRESSION:
STUnaryExpressionNode unaryExpr = (STUnaryExpressionNode) node;
if (unaryExpr.unaryOperator.kind != SyntaxKind.PLUS_TOKEN &&
unaryExpr.unaryOperator.kind != SyntaxKind.MINUS_TOKEN) {
return false;
}
return isNumericLiteral(unaryExpr.expression);
default:
return false;
}
}
private boolean isNumericLiteral(STNode node) {
switch (node.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 STNode parseTypedDescOrExprStartsWithOpenBracket() {
startContext(ParserRuleContext.BRACKETED_LIST);
STNode openBracket = parseOpenBracket();
List<STNode> members = new ArrayList<>();
STNode memberEnd;
while (!isEndOfListConstructor(peek().kind)) {
STNode expr = parseTypeDescOrExpr();
members.add(expr);
memberEnd = parseBracketedListMemberEnd();
if (memberEnd == null) {
break;
}
members.add(memberEnd);
}
STNode memberNodes = STNodeFactory.createNodeList(members);
STNode closeBracket = parseCloseBracket();
endContext();
return STNodeFactory.createTupleTypeDescriptorNode(openBracket, memberNodes, closeBracket);
}
/**
* 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 parseBindingPatternStartsWithIdentifier();
case OPEN_BRACE_TOKEN:
return parseMappingBindingPattern();
case ERROR_KEYWORD:
return parseErrorBindingPattern();
default:
Solution sol = recover(peek(), ParserRuleContext.BINDING_PATTERN);
if (sol.action == Action.REMOVE) {
return sol.recoveredNode;
}
return parseBindingPattern(sol.tokenKind);
}
}
private STNode parseBindingPatternStartsWithIdentifier() {
STNode argNameOrBindingPattern =
parseQualifiedIdentifier(ParserRuleContext.BINDING_PATTERN_STARTING_IDENTIFIER);
STToken secondToken = peek();
if (secondToken.kind == SyntaxKind.OPEN_PAREN_TOKEN) {
startContext(ParserRuleContext.FUNCTIONAL_BINDING_PATTERN);
return parseFunctionalBindingPattern(argNameOrBindingPattern);
}
if (argNameOrBindingPattern.kind != SyntaxKind.SIMPLE_NAME_REFERENCE) {
STNode identifier = SyntaxErrors.createMissingTokenWithDiagnostics(SyntaxKind.IDENTIFIER_TOKEN);
identifier = SyntaxErrors.cloneWithLeadingInvalidNodeMinutiae(identifier, argNameOrBindingPattern);
return createCaptureOrWildcardBP(identifier);
}
return createCaptureOrWildcardBP(((STSimpleNameReferenceNode) argNameOrBindingPattern).name);
}
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) {
if (isEndOfListBindingPattern(peek().kind) && bindingPatternsList.size() == 0) {
STNode closeBracket = parseCloseBracket();
STNode restBindingPattern = STNodeFactory.createEmptyNode();
STNode bindingPatternsNode = STNodeFactory.createNodeList(bindingPatternsList);
return STNodeFactory.createListBindingPatternNode(openBracket, bindingPatternsNode, restBindingPattern,
closeBracket);
}
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 = parseListBindingPatternMemberRhs(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 parseListBindingPatternMemberRhs() {
return parseListBindingPatternMemberRhs(peek().kind);
}
private STNode parseListBindingPatternMemberRhs(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_BRACKET_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.LIST_BINDING_PATTERN_MEMBER_END);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseListBindingPatternMemberRhs(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 member.
* <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 member
*/
private STNode parseListBindingPatternMember() {
STToken token = peek();
return parseListBindingPatternMember(token.kind);
}
private STNode parseListBindingPatternMember(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case ELLIPSIS_TOKEN:
return parseRestBindingPattern();
case OPEN_BRACKET_TOKEN:
case IDENTIFIER_TOKEN:
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
return parseBindingPattern();
default:
Solution sol = recover(peek(), ParserRuleContext.LIST_BINDING_PATTERN_MEMBER);
if (sol.action == Action.REMOVE) {
return sol.recoveredNode;
}
return parseListBindingPatternMember(sol.tokenKind);
}
}
/**
* Parse rest binding pattern.
* <p>
* <code>
* rest-binding-pattern := ... variable-name
* </code>
*
* @return Rest binding pattern node
*/
private STNode parseRestBindingPattern() {
startContext(ParserRuleContext.REST_BINDING_PATTERN);
STNode ellipsis = parseEllipsis();
STNode varName = parseVariableName();
endContext();
STSimpleNameReferenceNode simpleNameReferenceNode =
(STSimpleNameReferenceNode) STNodeFactory.createSimpleNameReferenceNode(varName);
return STNodeFactory.createRestBindingPatternNode(ellipsis, simpleNameReferenceNode);
}
/**
* 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, false);
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);
STNode openBrace = parseOpenBrace();
STToken token = peek();
if (isEndOfMappingBindingPattern(token.kind)) {
STNode closeBrace = parseCloseBrace();
STNode bindingPatternsNode = STNodeFactory.createEmptyNodeList();
STNode restBindingPattern = STNodeFactory.createEmptyNode();
endContext();
return STNodeFactory.createMappingBindingPatternNode(openBrace, bindingPatternsNode, restBindingPattern,
closeBrace);
}
List<STNode> bindingPatterns = new ArrayList<>();
STNode prevMember = parseMappingBindingPatternMember();
if (prevMember.kind != SyntaxKind.REST_BINDING_PATTERN) {
bindingPatterns.add(prevMember);
}
return parseMappingBindingPattern(openBrace, bindingPatterns, prevMember);
}
private STNode parseMappingBindingPattern(STNode openBrace, List<STNode> bindingPatterns, STNode prevMember) {
STToken token = peek();
STNode mappingBindingPatternRhs = null;
while (!isEndOfMappingBindingPattern(token.kind) && prevMember.kind != SyntaxKind.REST_BINDING_PATTERN) {
mappingBindingPatternRhs = parseMappingBindingPatternEnd(token.kind);
if (mappingBindingPatternRhs == null) {
break;
}
bindingPatterns.add(mappingBindingPatternRhs);
prevMember = parseMappingBindingPatternMember();
if (prevMember.kind == SyntaxKind.REST_BINDING_PATTERN) {
break;
}
bindingPatterns.add(prevMember);
token = peek();
}
STNode restBindingPattern;
if (prevMember.kind == SyntaxKind.REST_BINDING_PATTERN) {
restBindingPattern = prevMember;
} 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) {
STNode simpleNameReference = STNodeFactory.createSimpleNameReferenceNode(identifier);
if (peek().kind != SyntaxKind.COLON_TOKEN) {
return STNodeFactory.createFieldBindingPatternVarnameNode(simpleNameReference);
}
STNode colon = parseColon();
STNode bindingPattern = parseBindingPattern();
return STNodeFactory.createFieldBindingPatternFullNode(simpleNameReference, colon, bindingPattern);
}
private boolean isEndOfMappingBindingPattern(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case CLOSE_BRACE_TOKEN:
return true;
default:
return false;
}
}
/**
* Parse error binding pattern node.
* <p>
* <code>functional-binding-pattern := error ( arg-list-binding-pattern )</code>
*
* @return Error binding pattern node.
*/
private STNode parseErrorBindingPattern() {
startContext(ParserRuleContext.FUNCTIONAL_BINDING_PATTERN);
STNode typeDesc = parseErrorKeyword();
return parseFunctionalBindingPattern(typeDesc);
}
/**
* Parse functional binding pattern.
* <p>
* <code>
* functional-binding-pattern := functionally-constructible-type-reference ( arg-list-binding-pattern )
* <br/><br/>
* functionally-constructible-type-reference := error | type-reference
* </code>
*
* @param typeDesc Functionally constructible type reference
* @return Functional binding pattern node.
*/
private STNode parseFunctionalBindingPattern(STNode typeDesc) {
STNode openParenthesis = parseOpenParenthesis(ParserRuleContext.ARG_LIST_START);
STNode argListBindingPatterns = parseArgListBindingPatterns();
STNode closeParenthesis = parseCloseParenthesis();
endContext();
return STNodeFactory.createFunctionalBindingPatternNode(typeDesc, openParenthesis, argListBindingPatterns,
closeParenthesis);
}
private STNode parseArgListBindingPatterns() {
List<STNode> argListBindingPatterns = new ArrayList<>();
SyntaxKind lastValidArgKind = SyntaxKind.CAPTURE_BINDING_PATTERN;
STToken nextToken = peek();
if (isEndOfParametersList(nextToken.kind)) {
return STNodeFactory.createNodeList(argListBindingPatterns);
}
argListBindingPatterns.add(parseArgBindingPattern());
nextToken = peek();
while (!isEndOfParametersList(nextToken.kind)) {
STNode argEnd = parseArgsBindingPatternEnd(nextToken.kind);
if (argEnd == null) {
break;
}
nextToken = peek();
STNode currentArg = parseArgBindingPattern(nextToken.kind);
DiagnosticErrorCode errorCode = validateArgBindingPatternOrder(lastValidArgKind, currentArg.kind);
if (errorCode == null) {
argListBindingPatterns.add(argEnd);
argListBindingPatterns.add(currentArg);
lastValidArgKind = currentArg.kind;
} else {
updateLastNodeInListWithInvalidNode(argListBindingPatterns, argEnd, null);
updateLastNodeInListWithInvalidNode(argListBindingPatterns, currentArg, errorCode);
}
nextToken = peek();
}
return STNodeFactory.createNodeList(argListBindingPatterns);
}
private STNode parseArgsBindingPatternEnd() {
STToken nextToken = peek();
return parseArgsBindingPatternEnd(nextToken.kind);
}
private STNode parseArgsBindingPatternEnd(SyntaxKind nextTokenKind) {
switch (nextTokenKind) {
case COMMA_TOKEN:
return parseComma();
case CLOSE_PAREN_TOKEN:
return null;
default:
Solution solution = recover(peek(), ParserRuleContext.ARG_BINDING_PATTERN_END);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseArgsBindingPatternEnd(solution.tokenKind);
}
}
/**
* Parse arg binding pattern.
* <p>
* <code>
* arg-list-binding-pattern := positional-arg-binding-patterns [, other-arg-binding-patterns]
*                             | other-arg-binding-patterns
* <br/><br/>
* positional-arg-binding-patterns := positional-arg-binding-pattern (, positional-arg-binding-pattern)*
* <br/><br/>
* positional-arg-binding-pattern := binding-pattern
* <br/><br/>
* other-arg-binding-patterns := named-arg-binding-patterns [, rest-binding-pattern] | [rest-binding-pattern]
* <br/><br/>
* named-arg-binding-patterns := named-arg-binding-pattern (, named-arg-binding-pattern)*
* <br/><br/>
* named-arg-binding-pattern := arg-name = binding-pattern
* </code>
*
* @return Arg binding pattern
*/
private STNode parseArgBindingPattern() {
STToken nextToken = peek();
return parseArgBindingPattern(nextToken.kind);
}
private STNode parseArgBindingPattern(SyntaxKind kind) {
switch (kind) {
case ELLIPSIS_TOKEN:
return parseRestBindingPattern();
case IDENTIFIER_TOKEN:
return parseNamedOrPositionalArgBindingPattern(kind);
case OPEN_BRACKET_TOKEN:
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
return parseBindingPattern();
default:
Solution solution = recover(peek(), ParserRuleContext.ARG_BINDING_PATTERN);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseArgBindingPattern(solution.tokenKind);
}
}
private STNode parseNamedOrPositionalArgBindingPattern(SyntaxKind nextTokenKind) {
STNode argNameOrBindingPattern = parseQualifiedIdentifier(ParserRuleContext.ARG_BINDING_PATTERN_START_IDENT);
STToken secondToken = peek();
switch (secondToken.kind) {
case EQUAL_TOKEN:
STNode equal = parseAssignOp();
STNode bindingPattern = parseBindingPattern();
return STNodeFactory.createNamedArgBindingPatternNode(argNameOrBindingPattern, equal, bindingPattern);
case OPEN_PAREN_TOKEN:
return parseFunctionalBindingPattern(argNameOrBindingPattern);
case COMMA_TOKEN:
case CLOSE_PAREN_TOKEN:
default:
return createCaptureOrWildcardBP(argNameOrBindingPattern);
}
}
private DiagnosticErrorCode validateArgBindingPatternOrder(SyntaxKind prevArgKind, SyntaxKind currentArgKind) {
DiagnosticErrorCode errorCode = null;
switch (prevArgKind) {
case CAPTURE_BINDING_PATTERN:
case LIST_BINDING_PATTERN:
case MAPPING_BINDING_PATTERN:
break;
case NAMED_ARG_BINDING_PATTERN:
if (currentArgKind != SyntaxKind.NAMED_ARG_BINDING_PATTERN &&
currentArgKind != SyntaxKind.REST_BINDING_PATTERN) {
errorCode = DiagnosticErrorCode.ERROR_NAMED_ARG_FOLLOWED_BY_POSITIONAL_ARG;
}
break;
case REST_BINDING_PATTERN:
errorCode = DiagnosticErrorCode.ERROR_ARG_FOLLOWED_BY_REST_ARG;
break;
default:
throw new IllegalStateException("Invalid SyntaxKind in an argument");
}
return errorCode;
}
/*
* 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) {
STToken nextToken = peek();
return parseTypedBindingPatternTypeRhs(nextToken.kind, typeDesc, context, true);
}
private STNode parseTypedBindingPatternTypeRhs(STNode typeDesc, ParserRuleContext context, boolean isRoot) {
STToken nextToken = peek();
return parseTypedBindingPatternTypeRhs(nextToken.kind, typeDesc, context, isRoot);
}
private STNode parseTypedBindingPatternTypeRhs(SyntaxKind nextTokenKind, STNode typeDesc, ParserRuleContext context,
boolean isRoot) {
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, true, context);
assert typedBindingPattern.kind == SyntaxKind.TYPED_BINDING_PATTERN;
return typedBindingPattern;
case CLOSE_PAREN_TOKEN:
case COMMA_TOKEN:
case CLOSE_BRACKET_TOKEN:
case CLOSE_BRACE_TOKEN:
if (!isRoot) {
return typeDesc;
}
default:
Solution solution =
recover(peek(), ParserRuleContext.TYPED_BINDING_PATTERN_TYPE_RHS, typeDesc, context, isRoot);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseTypedBindingPatternTypeRhs(solution.tokenKind, typeDesc, context, isRoot);
}
}
/**
* 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,
boolean allowAssignment, 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, false);
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, allowAssignment, context);
}
private STNode parseAsMemberAccessExpr(STNode typeNameOrExpr, STNode openBracket, STNode member) {
member = parseExpressionRhs(DEFAULT_OP_PRECEDENCE, member, false, true);
STNode closeBracket = parseCloseBracket();
endContext();
STNode keyExpr = STNodeFactory.createNodeList(member);
STNode memberAccessExpr =
STNodeFactory.createIndexedExpressionNode(typeNameOrExpr, openBracket, keyExpr, closeBracket);
return parseExpressionRhs(DEFAULT_OP_PRECEDENCE, memberAccessExpr, false, 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:
case STRING_LITERAL:
return parseBasicLiteral();
case CLOSE_BRACKET_TOKEN:
return STNodeFactory.createEmptyNode();
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
case ELLIPSIS_TOKEN:
case OPEN_BRACKET_TOKEN:
return parseStatementStartBracketedListMember();
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);
typeDesc = validateForUsageOfVar(typeDesc);
STNode closeBracket = parseCloseBracket();
endContext();
return parseTypedBindingPatternOrMemberAccessRhs(typeDesc, openBracket, member, closeBracket, true, 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,
boolean allowAssignment, ParserRuleContext context) {
STToken nextToken = peek();
return parseTypedBindingPatternOrMemberAccessRhs(nextToken.kind, typeDescOrExpr, openBracket, member,
closeBracket, isTypedBindingPattern, allowAssignment, context);
}
private STNode parseTypedBindingPatternOrMemberAccessRhs(SyntaxKind nextTokenKind, STNode typeDescOrExpr,
STNode openBracket, STNode member, STNode closeBracket,
boolean isTypedBindingPattern, boolean allowAssignment,
ParserRuleContext context) {
switch (nextTokenKind) {
case IDENTIFIER_TOKEN:
case OPEN_BRACE_TOKEN:
case ERROR_KEYWORD:
STNode typeDesc = getTypeDescFromExpr(typeDescOrExpr);
STNode arrayTypeDesc = getArrayTypeDesc(openBracket, member, closeBracket, typeDesc);
return parseTypedBindingPatternTypeRhs(arrayTypeDesc, context);
case OPEN_BRACKET_TOKEN:
if (isTypedBindingPattern) {
typeDesc = getTypeDescFromExpr(typeDescOrExpr);
arrayTypeDesc = createArrayTypeDesc(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, allowAssignment, context);
case QUESTION_MARK_TOKEN:
typeDesc = getTypeDescFromExpr(typeDescOrExpr);
arrayTypeDesc = getArrayTypeDesc(openBracket, member, closeBracket, typeDesc);
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, isTypedBindingPattern);
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 || !allowAssignment || !isValidLVExpr(typeDescOrExpr)) {
return createTypedBindingPattern(typeDescOrExpr, openBracket, member, closeBracket);
}
keyExpr = STNodeFactory.createNodeList(member);
typeDescOrExpr = getExpression(typeDescOrExpr);
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, closeBracket.kind)) {
keyExpr = STNodeFactory.createNodeList(member);
typeDescOrExpr = getExpression(typeDescOrExpr);
return STNodeFactory.createIndexedExpressionNode(typeDescOrExpr, openBracket, keyExpr,
closeBracket);
}
break;
}
Solution solution = recover(peek(), ParserRuleContext.BRACKETED_LIST_RHS, typeDescOrExpr, openBracket, member,
closeBracket, isTypedBindingPattern, allowAssignment, context);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseTypedBindingPatternOrMemberAccessRhs(solution.tokenKind, typeDescOrExpr, openBracket, member,
closeBracket, isTypedBindingPattern, allowAssignment, context);
}
private STNode createTypedBindingPattern(STNode typeDescOrExpr, STNode openBracket, STNode member,
STNode closeBracket) {
STNode bindingPatterns;
if (isEmpty(member)) {
bindingPatterns = STNodeFactory.createEmptyNodeList();
} else {
STNode bindingPattern = getBindingPattern(member);
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-desc/binary-expression that involves ambiguous
* bracketed list 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,
boolean isTypedBindingPattern) {
STNode pipeOrAndToken = parseUnionOrIntersectionToken();
STNode typedBindingPatternOrExpr = parseTypedBindingPatternOrExpr(false);
if (isTypedBindingPattern || typedBindingPatternOrExpr.kind == SyntaxKind.TYPED_BINDING_PATTERN) {
STNode lhsTypeDesc = getTypeDescFromExpr(typeDescOrExpr);
lhsTypeDesc = getArrayTypeDesc(openBracket, member, closeBracket, lhsTypeDesc);
STTypedBindingPatternNode rhsTypedBindingPattern = (STTypedBindingPatternNode) typedBindingPatternOrExpr;
STNode newTypeDesc;
if (pipeOrAndToken.kind == SyntaxKind.PIPE_TOKEN) {
newTypeDesc = createUnionTypeDesc(lhsTypeDesc, pipeOrAndToken, rhsTypedBindingPattern.typeDescriptor);
} else {
newTypeDesc =
createIntersectionTypeDesc(lhsTypeDesc, pipeOrAndToken, rhsTypedBindingPattern.typeDescriptor);
}
return STNodeFactory.createTypedBindingPatternNode(newTypeDesc, rhsTypedBindingPattern.bindingPattern);
} else {
STNode keyExpr = getExpression(member);
STNode containerExpr = getExpression(typeDescOrExpr);
STNode lhsExpr =
STNodeFactory.createIndexedExpressionNode(containerExpr, openBracket, keyExpr, closeBracket);
return STNodeFactory.createBinaryExpressionNode(SyntaxKind.BINARY_EXPRESSION, lhsExpr, pipeOrAndToken,
typedBindingPatternOrExpr);
}
}
private STNode getArrayTypeDesc(STNode openBracket, STNode member, STNode closeBracket, STNode lhsTypeDesc) {
if (lhsTypeDesc.kind == SyntaxKind.UNION_TYPE_DESC) {
STUnionTypeDescriptorNode unionTypeDesc = (STUnionTypeDescriptorNode) lhsTypeDesc;
STNode middleTypeDesc = getArrayTypeDesc(openBracket, member, closeBracket, unionTypeDesc.rightTypeDesc);
lhsTypeDesc = createUnionTypeDesc(unionTypeDesc.leftTypeDesc, unionTypeDesc.pipeToken, middleTypeDesc);
} else if (lhsTypeDesc.kind == SyntaxKind.INTERSECTION_TYPE_DESC) {
STIntersectionTypeDescriptorNode intersectionTypeDesc = (STIntersectionTypeDescriptorNode) lhsTypeDesc;
STNode middleTypeDesc =
getArrayTypeDesc(openBracket, member, closeBracket, intersectionTypeDesc.rightTypeDesc);
lhsTypeDesc = createIntersectionTypeDesc(intersectionTypeDesc.leftTypeDesc,
intersectionTypeDesc.bitwiseAndToken, middleTypeDesc);
} else {
lhsTypeDesc = createArrayTypeDesc(lhsTypeDesc, openBracket, member, closeBracket);
}
return lhsTypeDesc;
}
/**
* 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 (isDefiniteTypeDesc(memberNode.kind)) {
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:
case MAPPING_BP_OR_MAPPING_CONSTRUCTOR:
return SyntaxKind.NONE;
default:
return SyntaxKind.INDEXED_EXPRESSION;
}
}
/*
* 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);
return parseStatementStartsWithOpenBracket(annots, true, possibleMappingField);
}
private STNode parseMemberBracketedList(boolean possibleMappingField) {
STNode annots = STNodeFactory.createEmptyNodeList();
return parseStatementStartsWithOpenBracket(annots, false, possibleMappingField);
}
/**
* 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 parseStatementStartsWithOpenBracket(STNode annots, boolean isRoot, boolean possibleMappingField) {
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(annots, openBracket, memberList, member, isRoot);
case LIST_BINDING_PATTERN:
return parseAsListBindingPattern(openBracket, memberList, member, isRoot);
case LIST_CONSTRUCTOR:
return parseAsListConstructor(openBracket, memberList, member, isRoot);
case LIST_BP_OR_LIST_CONSTRUCTOR:
return parseAsListBindingPatternOrListConstructor(openBracket, memberList, member, isRoot);
case TUPLE_TYPE_DESC_OR_LIST_CONST:
return parseAsTupleTypeDescOrListConstructor(annots, openBracket, memberList, member, isRoot);
case NONE:
default:
memberList.add(member);
break;
}
STNode memberEnd = parseBracketedListMemberEnd();
if (memberEnd == null) {
break;
}
memberList.add(memberEnd);
}
STNode closeBracket = parseCloseBracket();
STNode bracketedList = parseStatementStartBracketedList(annots, openBracket, memberList, closeBracket, isRoot,
possibleMappingField);
return bracketedList;
}
private STNode parseStatementStartBracketedListMember() {
STToken nextToken = peek();
return parseStatementStartBracketedListMember(nextToken.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 parseMemberBracketedList(false);
case IDENTIFIER_TOKEN:
STNode identifier = parseQualifiedIdentifier(ParserRuleContext.VARIABLE_REF);
if (isWildcardBP(identifier)) {
STNode varName = ((STSimpleNameReferenceNode) identifier).name;
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);
case OPEN_PAREN_TOKEN:
return parseTypeDescOrExpr();
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 parseAsTupleTypeDescOrListConstructor(STNode annots, STNode openBracket, List<STNode> memberList,
STNode member, boolean isRoot) {
memberList.add(member);
STNode memberEnd = parseBracketedListMemberEnd();
STNode tupleTypeDescOrListCons;
if (memberEnd == null) {
STNode closeBracket = parseCloseBracket();
tupleTypeDescOrListCons =
parseTupleTypeDescOrListConstructorRhs(openBracket, memberList, closeBracket, isRoot);
} else {
memberList.add(memberEnd);
tupleTypeDescOrListCons = parseTupleTypeDescOrListConstructor(annots, openBracket, memberList, isRoot);
}
return tupleTypeDescOrListCons;
}
/**
* Parse tuple type desc or list constructor.
*
* @return Parsed node
*/
private STNode parseTupleTypeDescOrListConstructor(STNode annots) {
startContext(ParserRuleContext.BRACKETED_LIST);
STNode openBracket = parseOpenBracket();
List<STNode> memberList = new ArrayList<>();
return parseTupleTypeDescOrListConstructor(annots, openBracket, memberList, false);
}
private STNode parseTupleTypeDescOrListConstructor(STNode annots, STNode openBracket, List<STNode> memberList,
boolean isRoot) {
STToken nextToken = peek();
while (!isBracketedListEnd(nextToken.kind)) {
STNode member = parseTupleTypeDescOrListConstructorMember(nextToken.kind, annots);
SyntaxKind currentNodeType = getParsingNodeTypeOfTupleTypeOrListCons(member);
switch (currentNodeType) {
case LIST_CONSTRUCTOR:
return parseAsListConstructor(openBracket, memberList, member, isRoot);
case TUPLE_TYPE_DESC:
return parseAsTupleTypeDesc(annots, openBracket, memberList, member, isRoot);
case TUPLE_TYPE_DESC_OR_LIST_CONST:
default:
memberList.add(member);
break;
}
STNode memberEnd = parseBracketedListMemberEnd();
if (memberEnd == null) {
break;
}
memberList.add(memberEnd);
nextToken = peek();
}
STNode closeBracket = parseCloseBracket();
return parseTupleTypeDescOrListConstructorRhs(openBracket, memberList, closeBracket, isRoot);
}
private STNode parseTupleTypeDescOrListConstructorMember(SyntaxKind nextTokenKind, STNode annots) {
switch (nextTokenKind) {
case OPEN_BRACKET_TOKEN:
return parseTupleTypeDescOrListConstructor(annots);
case IDENTIFIER_TOKEN:
STNode identifier = parseQualifiedIdentifier(ParserRuleContext.VARIABLE_REF);
nextTokenKind = peek().kind;
if (nextTokenKind == SyntaxKind.ELLIPSIS_TOKEN) {
STNode ellipsis = parseEllipsis();
return STNodeFactory.createRestDescriptorNode(identifier, ellipsis);
}
return parseExpressionRhs(DEFAULT_OP_PRECEDENCE, identifier, false, false);
case OPEN_BRACE_TOKEN:
return parseMappingConstructorExpr();
case ERROR_KEYWORD:
if (getNextNextToken(nextTokenKind).kind == SyntaxKind.OPEN_PAREN_TOKEN) {
return parseErrorConstructorExpr();
}
return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TUPLE);
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);
case OPEN_PAREN_TOKEN:
return parseTypeDescOrExpr();
default:
if (isValidExpressionStart(nextTokenKind, 1)) {
return parseExpression(false);
}
if (isTypeStartingToken(nextTokenKind)) {
return parseTypeDescriptor(ParserRuleContext.TYPE_DESC_IN_TUPLE);
}
Solution solution = recover(peek(), ParserRuleContext.TUPLE_TYPE_DESC_OR_LIST_CONST_MEMBER);
if (solution.action == Action.REMOVE) {
return solution.recoveredNode;
}
return parseStatementStartBracketedListMember(solution.tokenKind);
}
}
private SyntaxKind getParsingNodeTypeOfTupleTypeOrListCons(STNode memberNode) {
return getStmtStartBracketedListType(memberNode);
}
private STNode parseTupleTypeDescOrListConstructorRhs(STNode openBracket, List<STNode> members, STNode closeBracket,
boolean isRoot) {
STNode tupleTypeOrListConst;
switch (peek().kind) {
case COMMA_TOKEN:
case CLOSE_BRACE_TOKEN:
case CLOSE_BRACKET_TOKEN:
if (!isRoot) {
endContext();
return new STAmbiguousCollectionNode(SyntaxKind.TUPLE_TYPE_DESC_OR_LIST_CONST, openBracket, members,
closeBracket);
}
default:
if (isValidExprRhsStart(peek().kind, closeBracket.kind) ||
(isRoot && peek().kind == SyntaxKind.EQUAL_TOKEN)) {
members = getExpressionList(members);
STNode memberExpressions = STNodeFactory.createNodeList(members);
tupleTypeOrListConst = STNodeFactory.createListConstructorExpressionNode(openBracket,
memberExpressions, closeBracket);
break;
}
STNode memberTypeDescs = STNodeFactory.createNodeList(getTypeDescList(members));
STNode tupleTypeDesc =
STNodeFactory.createTupleTypeDescriptorNode(openBracket, memberTypeDescs, closeBracket);
tupleTypeOrListConst =
parseComplexTypeDescriptor(tupleTypeDesc, ParserRuleContext.TYPE_DESC_IN_TUPLE, false);
}
endContext();
if (!isRoot) {
return tupleTypeOrListConst;
}
return parseStmtStartsWithTupleTypeOrExprRhs(null, tupleTypeOrListConst, isRoot);
}