Semantic Tests refactored
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run

This commit is contained in:
Lucas 2024-06-26 16:21:04 +02:00
parent eb313464f0
commit d594bacb7d
24 changed files with 242 additions and 302 deletions

View File

@ -1,7 +1,7 @@
package semantic;
import ast.type.type.*;
import semantic.exeptions.AlreadyDeclearedException;
import semantic.exceptions.AlreadyDeclearedException;
import java.util.HashMap;
import java.util.Stack;

View File

@ -25,7 +25,7 @@ import ast.statement.statementexpression.crementExpression.IncrementExpressionNo
import ast.statement.statementexpression.methodcallstatementnexpression.MethodCallStatementExpressionNode;
import ast.type.type.*;
import semantic.context.Context;
import semantic.exeptions.*;
import semantic.exceptions.*;
import typechecker.TypeCheckResult;
public class SemanticAnalyzer implements SemanticVisitor {

View File

@ -1,4 +1,4 @@
package semantic.exeptions;
package semantic.exceptions;
public class AlreadyDeclearedException extends RuntimeException {

View File

@ -1,4 +1,4 @@
package semantic.exeptions;
package semantic.exceptions;
public class AlreadyDefinedException extends RuntimeException {

View File

@ -1,4 +1,4 @@
package semantic.exeptions;
package semantic.exceptions;
public class MultipleReturnTypes extends RuntimeException {

View File

@ -1,4 +1,4 @@
package semantic.exeptions;
package semantic.exceptions;
public class NotDeclearedException extends RuntimeException {

View File

@ -1,4 +1,4 @@
package semantic.exeptions;
package semantic.exceptions;
public class TypeMismatchException extends RuntimeException {

View File

@ -0,0 +1,9 @@
package semantic.exceptions;
public class UnknownException extends RuntimeException {
public UnknownException(String message) {
super(message);
}
}

View File

@ -1,282 +0,0 @@
package semantic;
import ast.ASTNode;
import ast.ProgramNode;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import parser.astBuilder.ASTBuilder;
import parser.generated.SimpleJavaLexer;
import parser.generated.SimpleJavaParser;
import semantic.exeptions.AlreadyDeclearedException;
import semantic.exeptions.MultipleReturnTypes;
import semantic.exeptions.NotDeclearedException;
import semantic.exeptions.TypeMismatchException;
import java.io.IOException;
import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.*;
public class EndToTAST {
@BeforeEach
public void setup(){
SemanticAnalyzer.clearAnalyzer();
}
@Test
public void CorrectTest(){
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/test/resources/semantic/endToTAST/CorrectTest.java"));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program(); // parse the input
/* ------------------------- AST builder -> AST ------------------------- */
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
assertEquals(SemanticAnalyzer.errors.size(), 0);
assertNotNull(tast);
}
@Test
public void notDecleared() {
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/test/resources/semantic/endToTAST/NotDecleared.java"));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program(); // parse the input
/* ------------------------- AST builder -> AST ------------------------- */
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
assertFalse(SemanticAnalyzer.errors.isEmpty());
assertInstanceOf(NotDeclearedException.class, SemanticAnalyzer.errors.getFirst());
}
@Test
public void typeMismatch(){
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/test/resources/semantic/endToTAST/TypeMismatchIntBool.java"));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
assertFalse(SemanticAnalyzer.errors.isEmpty());
assertInstanceOf(TypeMismatchException.class, SemanticAnalyzer.errors.getFirst());
}
@Test
public void parameterAlreadyDecleared(){
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/test/resources/semantic/endToTAST/ParameterAlreadyDecleared.java"));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
assertFalse(SemanticAnalyzer.errors.isEmpty());
assertInstanceOf(AlreadyDeclearedException.class, SemanticAnalyzer.errors.getFirst());
}
@Test
public void fieldAlreadyDecleared(){
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/test/resources/semantic/endToTAST/FieldAlreadyDecleared.java"));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
assertFalse(SemanticAnalyzer.errors.isEmpty());
assertInstanceOf(AlreadyDeclearedException.class, SemanticAnalyzer.errors.getFirst());
}
@Test
public void typeMismatchRefType(){
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/test/resources/semantic/endToTAST/TypeMismatchRefType.java"));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
assertFalse(SemanticAnalyzer.errors.isEmpty());
assertInstanceOf(TypeMismatchException.class, SemanticAnalyzer.errors.getFirst());
}
@Test
public void correctRetType(){
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/test/resources/semantic/endToTAST/CorrectRetType.java"));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
assertTrue(SemanticAnalyzer.errors.isEmpty());
}
@Test
public void retTypeMismatch(){
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/test/resources/semantic/endToTAST/retTypeMismatch.java"));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
assertFalse(SemanticAnalyzer.errors.isEmpty());
assertInstanceOf(TypeMismatchException.class, SemanticAnalyzer.errors.getFirst());
}
@Test
public void multipleRetType(){
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/test/resources/semantic/endToTAST/MultipleRetTypes.java"));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
assertFalse(SemanticAnalyzer.errors.isEmpty());
assertInstanceOf(MultipleReturnTypes.class, SemanticAnalyzer.errors.getFirst());
}
@Test
public void wrongTypeInIfClause(){
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/test/resources/semantic/endToTAST/WrongIfClause.java"));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree);
assertFalse(SemanticAnalyzer.errors.isEmpty());
}
}

View File

@ -0,0 +1,218 @@
package semantic;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.tree.ParseTree;
import ast.ASTNode;
import ast.ProgramNode;
import parser.astBuilder.ASTBuilder;
import parser.generated.SimpleJavaLexer;
import parser.generated.SimpleJavaParser;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
public class EndToTypedAstTest {
private static final Map<String, Class<?>> exceptionMap = new HashMap<>();
@Test
public void exceptionsTest() {
String directoryPath = "src/test/resources/input/typedAstExceptionsTests";
File folder = new File(directoryPath);
try {
loadCustomExceptions();
System.out.println("Custom exceptions loaded successfully.");
} catch (Exception e) {
throw new RuntimeException("Failed to load custom exceptions", e);
}
if (folder.isDirectory()) {
File[] files = folder.listFiles((_, name) -> name.endsWith(".java"));
if (files != null) {
for (File file : files) {
String expectedException = extractExpectedException(file);
SemanticAnalyzer.clearAnalyzer();
CharStream codeCharStream;
try {
codeCharStream = CharStreams.fromPath(Paths.get(file.getPath()));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode typedAst = SemanticAnalyzer.generateTast(abstractSyntaxTree);
if (expectedException != null) {
System.out.println("Testing the file: " + file.getName());
assertFalse(SemanticAnalyzer.errors.isEmpty(), "Expected an exception, but none was found.");
assertInstanceOf(getExceptionClass(expectedException), SemanticAnalyzer.errors.getFirst());
} else {
System.out.println("No expected exception specified.");
// If no expected exception is specified, you might want to add a different check
// e.g., assertTrue(SemanticAnalyzer.errors.isEmpty(), "No exceptions expected, but some were found.");
}
}
} else {
System.out.println("No files found in the directory.");
}
} else {
System.out.println("The provided path is not a directory.");
}
}
@Test
public void featureTest(){
String directoryPath = "src/test/resources/input/typedAstFeaturesTests";
File folder = new File(directoryPath);
if (folder.isDirectory()) {
File[] files = folder.listFiles((_, name) -> name.endsWith(".java"));
if (files != null) {
for (File file : files) {
SemanticAnalyzer.clearAnalyzer();
CharStream codeCharStream;
try {
codeCharStream = CharStreams.fromPath(Paths.get(file.getPath()));
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(codeCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
ASTNode typedAst = SemanticAnalyzer.generateTast(abstractSyntaxTree);
System.out.println("Testing the file: " + file.getName());
assertTrue(SemanticAnalyzer.errors.isEmpty());
assertNotNull(typedAst);
}
} else {
System.out.println("No files found in the directory.");
}
} else {
System.out.println("The provided path is not a directory.");
}
}
// ------------------ Helpers ------------------
/**
* This method is used to extract the expected exception from a given file.
* It reads the file line by line and uses a regular expression to match the expected exception annotation.
* The expected exception annotation should be in the format: "// @expected: ExceptionName".
* If the expected exception annotation is found, it returns the name of the expected exception.
* If the expected exception annotation is not found, it returns null.
*
* @param file The file from which the expected exception is to be extracted.
* @return The name of the expected exception, or null if the expected exception annotation is not found.
*/
private String extractExpectedException(File file) {
String annotationPattern = "//\\s*@expected:\\s*(\\S+)";
Pattern pattern = Pattern.compile(annotationPattern);
try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
String line;
while ((line = reader.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (matcher.find()) {
return matcher.group(1);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* This method is used to retrieve the Class object associated with a given exception name.
* It first prints the original exception name, then appends the package name to the exception name and prints it.
* It then retrieves the Class object from the exceptionMap using the fully qualified exception name.
* If the Class object is not found in the exceptionMap, it throws a RuntimeException.
*
* @param exceptionName The name of the exception for which the Class object is to be retrieved.
* @return The Class object associated with the given exception name.
* @throws RuntimeException If the Class object for the given exception name is not found in the exceptionMap.
*/
private Class<?> getExceptionClass(String exceptionName) {
System.out.println(exceptionName);
exceptionName = "semantic.exceptions." + exceptionName;
System.out.println(exceptionName);
Class<?> exceptionClass = exceptionMap.get(exceptionName);
if (exceptionClass == null) {
throw new RuntimeException("Exception class not found: " + exceptionName);
}
return exceptionClass;
}
/**
* This method is used to load custom exceptions from a specified package.
* It first constructs the directory path from the package name and checks if the directory exists.
* If the directory does not exist, it throws an IllegalArgumentException.
* It then creates a URLClassLoader to load the classes from the directory.
* It iterates over all the files in the directory, and for each file, it constructs the class name and loads the class.
* If the loaded class is a subtype of Throwable, it adds the class to the exceptionMap.
*
* @throws Exception If any error occurs during class loading.
*/
private static void loadCustomExceptions() throws Exception {
final String packName = "semantic.exceptions";
final String dirForMyClasses = "src/main/java/%s".formatted(packName.replace(".", "/"));
File folder = new File(dirForMyClasses);
if (!folder.isDirectory()) {
throw new IllegalArgumentException("The provided path is not a directory.");
}
URL[] urls = {folder.toURI().toURL()};
URLClassLoader classLoader;
try {
classLoader = new URLClassLoader(urls);
} catch (Exception e) {
throw new RuntimeException("Failed to create class loader", e);
}
for (File file : Objects.requireNonNull(folder.listFiles())) {
String className = packName + "." + file.getName().replaceAll("\\.(?:class|java)$", "");
System.out.printf("Loading custom exception: %s (=> %s)", file.getName(), className);
Class<?> cls = classLoader.loadClass(className);
if (Throwable.class.isAssignableFrom(cls)) { // Check if the class is a subtype of Throwable
exceptionMap.put(className, cls);
System.out.println("Loaded custom exception: " + className);
}
}
}
}

View File

@ -1,18 +1,5 @@
package semantic;
import ast.*;
import ast.member.FieldNode;
import ast.member.MemberNode;
import ast.member.MethodNode;
import ast.parameter.ParameterNode;
import org.junit.jupiter.api.Test;
import semantic.exeptions.AlreadyDeclearedException;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
public class SemanticTest {

View File

@ -1,3 +1,4 @@
// @expected: AlreadyDeclearedException
public class Example {
public int a;

View File

@ -1,3 +1,4 @@
// @expected: MultipleReturnTypes
public class Example {
public static int testMethod(int x, char c){

View File

@ -1,3 +1,4 @@
// @expected: NotDeclearedException
public class Test {
public static int testMethod(int x){
int a = b;

View File

@ -1,3 +1,4 @@
// @expected: AlreadyDeclearedException
public class Example {
public static int testMethod(char a, int a){

View File

@ -1,3 +1,4 @@
// @expected: TypeMismatchException
public class Example {
public static int testMethod(char x){

View File

@ -1,3 +1,4 @@
// @expected: TypeMismatchException
public class Test {
public boolean b;

View File

@ -1,3 +1,4 @@
// @expected: TypeMismatchException
public class Test {
public static int testMethod(ExampleA exampleA, ExampleB exampleB){

View File

@ -1,3 +1,4 @@
// @expected: TypeMismatchException
public class Example {
public static void testMethod(int x){