/*
* Copyright (c) 2012, the Dart project authors.
*
* Licensed under the Eclipse Public License v1.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
*
* http://www.eclipse.org/legal/epl-v10.html
*
* Unless required by applicable law or agreed to in writing, software distributed under the License
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
* or implied. See the License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.dart.engine.parser;
import com.google.dart.engine.EngineTestCase;
import com.google.dart.engine.ast.Annotation;
import com.google.dart.engine.ast.Comment;
import com.google.dart.engine.ast.CompilationUnit;
import com.google.dart.engine.ast.Expression;
import com.google.dart.engine.ast.Statement;
import com.google.dart.engine.error.AnalysisError;
import com.google.dart.engine.error.ErrorCode;
import com.google.dart.engine.error.GatheringErrorListener;
import com.google.dart.engine.internal.parser.CommentAndMetadata;
import com.google.dart.engine.scanner.CharSequenceReader;
import com.google.dart.engine.scanner.Scanner;
import com.google.dart.engine.scanner.Token;
import com.google.dart.engine.source.TestSource;
import junit.framework.AssertionFailedError;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class ParserTestCase extends EngineTestCase {
/**
* An empty array of objects used as arguments to zero-argument methods.
*/
private static final Object[] EMPTY_ARGUMENTS = new Object[0];
/**
* A flag indicating whether parser is to parse function bodies.
*/
protected static boolean parseFunctionBodies = true;
/**
* Create a parser.
*
* @param listener the listener to be passed to the parser
* @return the parser that was created
*/
public static Parser createParser(GatheringErrorListener listener) {
Parser parser = new Parser(null, listener);
parser.setParseAsync(true);
parser.setParseDeferredLibraries(true);
parser.setParseEnum(true);
return parser;
}
/**
* Invoke a parse method in {@link Parser}. The method is assumed to have the given number and
* type of parameters and will be invoked with the given arguments.
* <p>
* The given source is scanned and the parser is initialized to start with the first token in the
* source before the parse method is invoked.
*
* @param methodName the name of the parse method that should be invoked to parse the source
* @param objects the values of the arguments to the method
* @param source the source to be parsed by the parse method
* @return the result of invoking the method
* @throws Exception if the method could not be invoked or throws an exception
* @throws AssertionFailedError if the result is {@code null} or if any errors are produced
*/
public static <E> E parse(String methodName, Object[] objects, String source) throws Exception {
return parse(methodName, objects, source, new AnalysisError[0]);
}
/**
* Invoke a parse method in {@link Parser}. The method is assumed to have the given number and
* type of parameters and will be invoked with the given arguments.
* <p>
* The given source is scanned and the parser is initialized to start with the first token in the
* source before the parse method is invoked.
*
* @param methodName the name of the parse method that should be invoked to parse the source
* @param objects the values of the arguments to the method
* @param source the source to be parsed by the parse method
* @param errors the errors that should be generated
* @return the result of invoking the method
* @throws Exception if the method could not be invoked or throws an exception
* @throws AssertionFailedError if the result is {@code null} or the errors produced while
* scanning and parsing the source do not match the expected errors
*/
public static <E> E parse(String methodName, Object[] objects, String source,
AnalysisError... errors) throws Exception {
GatheringErrorListener listener = new GatheringErrorListener();
E result = invokeParserMethod(methodName, objects, source, listener);
listener.assertErrors(errors);
return result;
}
/**
* Invoke a parse method in {@link Parser}. The method is assumed to have the given number and
* type of parameters and will be invoked with the given arguments.
* <p>
* The given source is scanned and the parser is initialized to start with the first token in the
* source before the parse method is invoked.
*
* @param methodName the name of the parse method that should be invoked to parse the source
* @param objects the values of the arguments to the method
* @param source the source to be parsed by the parse method
* @param errorCodes the error codes of the errors that should be generated
* @return the result of invoking the method
* @throws Exception if the method could not be invoked or throws an exception
* @throws AssertionFailedError if the result is {@code null} or the errors produced while
* scanning and parsing the source do not match the expected errors
*/
public static <E> E parse(String methodName, Object[] objects, String source,
ErrorCode... errorCodes) throws Exception {
GatheringErrorListener listener = new GatheringErrorListener();
E result = invokeParserMethod(methodName, objects, source, listener);
listener.assertErrorsWithCodes(errorCodes);
return result;
}
/**
* Invoke a parse method in {@link Parser}. The method is assumed to have no arguments.
* <p>
* The given source is scanned and the parser is initialized to start with the first token in the
* source before the parse method is invoked.
*
* @param methodName the name of the parse method that should be invoked to parse the source
* @param source the source to be parsed by the parse method
* @param errorCodes the error codes of the errors that should be generated
* @return the result of invoking the method
* @throws Exception if the method could not be invoked or throws an exception
* @throws AssertionFailedError if the result is {@code null} or the errors produced while
* scanning and parsing the source do not match the expected errors
*/
public static <E> E parse(String methodName, String source, ErrorCode... errorCodes)
throws Exception {
return parse(methodName, EMPTY_ARGUMENTS, source, errorCodes);
}
/**
* Parse the given source as a compilation unit.
*
* @param source the source to be parsed
* @param errorCodes the error codes of the errors that are expected to be found
* @return the compilation unit that was parsed
* @throws Exception if the source could not be parsed, if the compilation errors in the source do
* not match those that are expected, or if the result would have been {@code null}
*/
public static CompilationUnit parseCompilationUnit(String source, ErrorCode... errorCodes)
throws Exception {
GatheringErrorListener listener = new GatheringErrorListener();
Scanner scanner = new Scanner(null, new CharSequenceReader(source), listener);
listener.setLineInfo(new TestSource(), scanner.getLineStarts());
Token token = scanner.tokenize();
Parser parser = createParser(listener);
CompilationUnit unit = parser.parseCompilationUnit(token);
assertNotNull(unit);
listener.assertErrorsWithCodes(errorCodes);
return unit;
}
/**
* Parse the given source as an expression.
*
* @param source the source to be parsed
* @param errorCodes the error codes of the errors that are expected to be found
* @return the expression that was parsed
* @throws Exception if the source could not be parsed, if the compilation errors in the source do
* not match those that are expected, or if the result would have been {@code null}
*/
@SuppressWarnings("unchecked")
public static <E extends Expression> E parseExpression(String source, ErrorCode... errorCodes)
throws Exception {
GatheringErrorListener listener = new GatheringErrorListener();
Scanner scanner = new Scanner(null, new CharSequenceReader(source), listener);
listener.setLineInfo(new TestSource(), scanner.getLineStarts());
Token token = scanner.tokenize();
Parser parser = createParser(listener);
Expression expression = parser.parseExpression(token);
assertNotNull(expression);
listener.assertErrorsWithCodes(errorCodes);
return (E) expression;
}
/**
* Parse the given source as a statement.
*
* @param source the source to be parsed
* @param errorCodes the error codes of the errors that are expected to be found
* @return the statement that was parsed
* @throws Exception if the source could not be parsed, if the compilation errors in the source do
* not match those that are expected, or if the result would have been {@code null}
*/
@SuppressWarnings("unchecked")
public static <E extends Statement> E parseStatement(String source, ErrorCode... errorCodes)
throws Exception {
GatheringErrorListener listener = new GatheringErrorListener();
Scanner scanner = new Scanner(null, new CharSequenceReader(source), listener);
listener.setLineInfo(new TestSource(), scanner.getLineStarts());
Token token = scanner.tokenize();
Parser parser = createParser(listener);
Statement statement = parser.parseStatement(token);
assertNotNull(statement);
listener.assertErrorsWithCodes(errorCodes);
return (E) statement;
}
/**
* Parse the given source as a sequence of statements.
*
* @param source the source to be parsed
* @param expectedCount the number of statements that are expected
* @param errorCodes the error codes of the errors that are expected to be found
* @return the statements that were parsed
* @throws Exception if the source could not be parsed, if the number of statements does not match
* the expected count, if the compilation errors in the source do not match those that
* are expected, or if the result would have been {@code null}
*/
public static List<Statement> parseStatements(String source, int expectedCount,
ErrorCode... errorCodes) throws Exception {
GatheringErrorListener listener = new GatheringErrorListener();
Scanner scanner = new Scanner(null, new CharSequenceReader(source), listener);
listener.setLineInfo(new TestSource(), scanner.getLineStarts());
Token token = scanner.tokenize();
Parser parser = createParser(listener);
List<Statement> statements = parser.parseStatements(token);
assertSizeOfList(expectedCount, statements);
listener.assertErrorsWithCodes(errorCodes);
return statements;
}
/**
* Invoke a method in {@link Parser}. The method is assumed to have the given number and type of
* parameters and will be invoked with the given arguments.
* <p>
* The given source is scanned and the parser is initialized to start with the first token in the
* source before the method is invoked.
*
* @param methodName the name of the method that should be invoked
* @param objects the values of the arguments to the method
* @param source the source to be processed by the parse method
* @param listener the error listener that will be used for both scanning and parsing
* @return the result of invoking the method
* @throws Exception if the method could not be invoked or throws an exception
* @throws AssertionFailedError if the result is {@code null} or the errors produced while
* scanning and parsing the source do not match the expected errors
*/
@SuppressWarnings("unchecked")
protected static <E> E invokeParserMethod(String methodName, Object[] objects, String source,
GatheringErrorListener listener) throws Exception {
//
// Scan the source.
//
Scanner scanner = new Scanner(null, new CharSequenceReader(source), listener);
Token tokenStream = scanner.tokenize();
listener.setLineInfo(new TestSource(), scanner.getLineStarts());
//
// Parse the source.
//
Parser parser = createParser(listener);
parser.setParseFunctionBodies(parseFunctionBodies);
parser.setParseDeferredLibraries(true);
parser.setParseAsync(true);
Object result = invokeParserMethodImpl(parser, methodName, objects, tokenStream);
//
// Partially test the results.
//
if (!listener.hasErrors()) {
assertNotNull(result);
}
return (E) result;
}
/**
* Invoke a method in {@link Parser}. The method is assumed to have no arguments.
* <p>
* The given source is scanned and the parser is initialized to start with the first token in the
* source before the method is invoked.
*
* @param methodName the name of the method that should be invoked
* @param source the source to be processed by the parse method
* @param listener the error listener that will be used for both scanning and parsing
* @return the result of invoking the method
* @throws Exception if the method could not be invoked or throws an exception
* @throws AssertionFailedError if the result is {@code null} or the errors produced while
* scanning and parsing the source do not match the expected errors
*/
protected static <E> E invokeParserMethod(String methodName, String source,
GatheringErrorListener listener) throws Exception {
return invokeParserMethod(methodName, EMPTY_ARGUMENTS, source, listener);
}
/**
* Invokes {@link Parser} method with given name and valid number of parameters for given
* arguments.
*/
protected static Object invokeParserMethodImpl(Parser parser, String methodName,
Object[] objects, Token tokenStream) throws Exception {
Field currentTokenField = Parser.class.getDeclaredField("currentToken");
currentTokenField.setAccessible(true);
currentTokenField.set(parser, tokenStream);
Method parseMethod = findParserMethod(methodName, objects.length);
return parseMethod.invoke(parser, objects);
}
/**
* @return the {@link Method} with given name in {@link Parser}, not {@code null}. Fails if not
* found.
*/
private static Method findParserMethod(String name, int numParameters) {
Method[] methods = Parser.class.getDeclaredMethods();
for (Method method : methods) {
if (method.getName().equals(name) && method.getParameterTypes().length == numParameters) {
method.setAccessible(true);
return method;
}
}
fail("Cannot find method Parser." + name);
return null;
}
/**
* Return a CommentAndMetadata object with the given values that can be used for testing.
*
* @param comment the comment to be wrapped in the object
* @param annotations the annotations to be wrapped in the object
* @return a CommentAndMetadata object that can be used for testing
*/
protected CommentAndMetadata commentAndMetadata(Comment comment, Annotation... annotations) {
ArrayList<Annotation> metadata = new ArrayList<Annotation>();
for (Annotation annotation : annotations) {
metadata.add(annotation);
}
return new CommentAndMetadata(comment, metadata);
}
/**
* Return an empty CommentAndMetadata object that can be used for testing.
*
* @return an empty CommentAndMetadata object that can be used for testing
*/
protected CommentAndMetadata emptyCommentAndMetadata() {
return new CommentAndMetadata(null, new ArrayList<Annotation>());
}
@Override
protected void setUp() throws Exception {
super.setUp();
parseFunctionBodies = true;
}
}