* </pre>
*
* @return the formal parameters that were parsed
*/
protected FormalParameterList parseFormalParameterList() {
Token leftParenthesis = expect(TokenType.OPEN_PAREN);
if (matches(TokenType.CLOSE_PAREN)) {
return new FormalParameterList(leftParenthesis, null, null, null, getAndAdvance());
}
//
// Even though it is invalid to have default parameters outside of brackets, required parameters
// inside of brackets, or multiple groups of default and named parameters, we allow all of these
// cases so that we can recover better.
//
List<FormalParameter> parameters = new ArrayList<FormalParameter>();
List<FormalParameter> normalParameters = new ArrayList<FormalParameter>();
List<FormalParameter> positionalParameters = new ArrayList<FormalParameter>();
List<FormalParameter> namedParameters = new ArrayList<FormalParameter>();
List<FormalParameter> currentParameters = normalParameters;
Token leftSquareBracket = null;
Token rightSquareBracket = null;
Token leftCurlyBracket = null;
Token rightCurlyBracket = null;
ParameterKind kind = ParameterKind.REQUIRED;
boolean firstParameter = true;
boolean reportedMuliplePositionalGroups = false;
boolean reportedMulipleNamedGroups = false;
boolean reportedMixedGroups = false;
boolean wasOptionalParameter = false;
Token initialToken = null;
do {
if (firstParameter) {
firstParameter = false;
} else if (!optional(TokenType.COMMA)) {
// TODO(brianwilkerson) The token is wrong, we need to recover from this case.
if (getEndToken(leftParenthesis) != null) {
reportErrorForCurrentToken(ParserErrorCode.EXPECTED_TOKEN, TokenType.COMMA.getLexeme());
} else {
reportErrorForToken(
ParserErrorCode.MISSING_CLOSING_PARENTHESIS,
currentToken.getPrevious());
break;
}
}
initialToken = currentToken;
//
// Handle the beginning of parameter groups.
//
if (matches(TokenType.OPEN_SQUARE_BRACKET)) {
wasOptionalParameter = true;
if (leftSquareBracket != null && !reportedMuliplePositionalGroups) {
reportErrorForCurrentToken(ParserErrorCode.MULTIPLE_POSITIONAL_PARAMETER_GROUPS);
reportedMuliplePositionalGroups = true;
}
if (leftCurlyBracket != null && !reportedMixedGroups) {
reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
reportedMixedGroups = true;
}
leftSquareBracket = getAndAdvance();
currentParameters = positionalParameters;
kind = ParameterKind.POSITIONAL;
} else if (matches(TokenType.OPEN_CURLY_BRACKET)) {
wasOptionalParameter = true;
if (leftCurlyBracket != null && !reportedMulipleNamedGroups) {
reportErrorForCurrentToken(ParserErrorCode.MULTIPLE_NAMED_PARAMETER_GROUPS);
reportedMulipleNamedGroups = true;
}
if (leftSquareBracket != null && !reportedMixedGroups) {
reportErrorForCurrentToken(ParserErrorCode.MIXED_PARAMETER_GROUPS);
reportedMixedGroups = true;
}
leftCurlyBracket = getAndAdvance();
currentParameters = namedParameters;
kind = ParameterKind.NAMED;
}
//
// Parse and record the parameter.
//
FormalParameter parameter = parseFormalParameter(kind);
parameters.add(parameter);
currentParameters.add(parameter);
if (kind == ParameterKind.REQUIRED && wasOptionalParameter) {
reportErrorForNode(ParserErrorCode.NORMAL_BEFORE_OPTIONAL_PARAMETERS, parameter);
}
//
// Handle the end of parameter groups.
//
// TODO(brianwilkerson) Improve the detection and reporting of missing and mismatched delimiters.
if (matches(TokenType.CLOSE_SQUARE_BRACKET)) {
rightSquareBracket = getAndAdvance();
currentParameters = normalParameters;
if (leftSquareBracket == null) {
if (leftCurlyBracket != null) {
reportErrorForCurrentToken(ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, "}");
rightCurlyBracket = rightSquareBracket;
rightSquareBracket = null;
} else {
reportErrorForCurrentToken(
ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
"[");
}
}
kind = ParameterKind.REQUIRED;
} else if (matches(TokenType.CLOSE_CURLY_BRACKET)) {
rightCurlyBracket = getAndAdvance();
currentParameters = normalParameters;
if (leftCurlyBracket == null) {
if (leftSquareBracket != null) {
reportErrorForCurrentToken(ParserErrorCode.WRONG_TERMINATOR_FOR_PARAMETER_GROUP, "]");
rightSquareBracket = rightCurlyBracket;
rightCurlyBracket = null;
} else {
reportErrorForCurrentToken(
ParserErrorCode.UNEXPECTED_TERMINATOR_FOR_PARAMETER_GROUP,
"{");
}
}
kind = ParameterKind.REQUIRED;
}
} while (!matches(TokenType.CLOSE_PAREN) && initialToken != currentToken);
Token rightParenthesis = expect(TokenType.CLOSE_PAREN);
//
// Check that the groups were closed correctly.
//
if (leftSquareBracket != null && rightSquareBracket == null) {
reportErrorForCurrentToken(ParserErrorCode.MISSING_TERMINATOR_FOR_PARAMETER_GROUP, "]");