Compare commits

..

14 Commits

Author SHA1 Message Date
f24e279596 Merge branch 'NewParser' into code-generator
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2024-06-27 17:33:50 -04:00
Purplumbi504
27f50b9c66 Adding EmptyClass Test AST
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
2024-06-27 21:14:02 +02:00
85ae06048c Add Return if Returntype is Void
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2024-06-27 18:55:28 +02:00
cd845cd91c Fixed Merge Conflicts
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2024-06-27 18:45:14 +02:00
78d5528733 Merge pull request 'More Tests, Structure, etc. Huge Changes' (#14) from Tests into main
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
Reviewed-on: #14
Reviewed-by: Johannes Ehlert <i22005@hb.dhbw-stuttgart.de>
2024-06-27 10:49:35 +00:00
Maximilian Stahl
88a25207e9 Added Interface and fixed bugs
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
2024-06-26 17:34:40 +02:00
Lucas
4e56760dd6 Old spelling mistake in semantic fixed
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled
2024-06-26 16:28:53 +02:00
Lucas
347bdcbd94 Update .gitignore
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2024-06-26 16:22:38 +02:00
Lucas
d594bacb7d Semantic Tests refactored
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2024-06-26 16:21:04 +02:00
Lucas
eb313464f0 Tests (structure) refactored
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2024-06-26 11:25:24 +02:00
Lucas
34d17660ef Tests refactored
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2024-06-25 18:05:58 +02:00
Lucas
cc6d26e17d Tests refactored
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2024-06-25 18:05:26 +02:00
Lucas
f9743efddc Merge branch 'main' into Tests
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2024-06-25 16:43:42 +02:00
Lucas
b37e065857 Small changes
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2024-06-25 16:41:04 +02:00
65 changed files with 587 additions and 558 deletions

1
.gitignore vendored
View File

@ -83,3 +83,4 @@ src/test/resources/output/javac/CompilerInput$Test.class
src/test/resources/output/javac/CompilerInput.class src/test/resources/output/javac/CompilerInput.class
src/test/resources/output/raupenpiler/CompilerInput.class src/test/resources/output/raupenpiler/CompilerInput.class
src/test/resources/output/raupenpiler/CompilerInput$Test.class src/test/resources/output/raupenpiler/CompilerInput$Test.class
.idea/inspectionProfiles/Project_Default.xml

View File

@ -15,30 +15,20 @@ public class ClassNode implements ASTNode, Visitable {
public AccessModifierNode accessType; public AccessModifierNode accessType;
public String identifier; public String identifier;
public List<MemberNode> members = new ArrayList<>(); public List<MemberNode> members = new ArrayList<>();
public boolean hasConstructor;
public ClassNode() {} public ClassNode(){
}
public ClassNode(String accessType, String identifier){ public ClassNode(String accessType, String identifier){
this.accessType = new AccessModifierNode(accessType); this.accessType = new AccessModifierNode(accessType);
this.identifier = identifier; this.identifier = identifier;
hasConstructor = false;
} }
public void addMember(MemberNode member) { public void addMember(MemberNode member) {
if (member instanceof ConstructorNode) {
this.hasConstructor = true;
}
members.add(member); members.add(member);
} }
public void ensureConstructor(){
if(!hasConstructor) {
ConstructorNode constructor = new ConstructorNode(new AccessModifierNode("public"), identifier);
members.addFirst(constructor);
}
}
public List<MethodNode> getMethods(){ public List<MethodNode> getMethods(){
List<MethodNode> methods = new ArrayList<>(); List<MethodNode> methods = new ArrayList<>();
for (MemberNode member : members) { for (MemberNode member : members) {

View File

@ -8,17 +8,13 @@ import visitor.Visitable;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class BlockNode implements ASTNode, Visitable { public class BlockNode implements IStatementNode, Visitable {
public List<IStatementNode> statements = new ArrayList<>(); public List<IStatementNode> statements = new ArrayList<>();
public Boolean hasReturnStatement = false;
public BlockNode() {} public BlockNode() {}
public void addStatement(IStatementNode statement) { public void addStatement(IStatementNode statement) {
statements.add(statement); statements.add(statement);
if(statement instanceof ReturnNode) {
hasReturnStatement = true;
}
} }
@Override @Override

View File

@ -1,8 +1,10 @@
package ast.statements; package ast.statements;
import ast.ASTNode; import ast.ASTNode;
import semantic.SemanticVisitor;
import typechecker.TypeCheckResult;
public class ElseNode implements ASTNode { public class ElseNode implements IStatementNode {
BlockNode block; BlockNode block;
public ElseNode(BlockNode block) { public ElseNode(BlockNode block) {
@ -10,4 +12,8 @@ public class ElseNode implements ASTNode {
} }
@Override
public TypeCheckResult accept(SemanticVisitor visitor) {
return null;
}
} }

View File

@ -1,11 +1,13 @@
package ast.statements; package ast.statements;
import ast.ASTNode; import ast.ASTNode;
import semantic.SemanticVisitor;
import typechecker.TypeCheckResult;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
public class IfElseNode implements ASTNode { public class IfElseNode implements IStatementNode {
IfNode ifStatement; IfNode ifStatement;
List<IfNode> elseIfStatements = new ArrayList<>(); List<IfNode> elseIfStatements = new ArrayList<>();
ElseNode elseStatement; ElseNode elseStatement;
@ -19,4 +21,8 @@ public class IfElseNode implements ASTNode {
elseIfStatements.add(elseIfStament); elseIfStatements.add(elseIfStament);
} }
@Override
public TypeCheckResult accept(SemanticVisitor visitor) {
return null;
}
} }

View File

@ -2,13 +2,20 @@ package ast.statements;
import ast.ASTNode; import ast.ASTNode;
import ast.expressions.IExpressionNode; import ast.expressions.IExpressionNode;
import semantic.SemanticVisitor;
import typechecker.TypeCheckResult;
public class IfNode implements ASTNode { public class IfNode implements IStatementNode {
public IExpressionNode expression; IExpressionNode expression;
public BlockNode block; BlockNode block;
public IfNode(IExpressionNode expression, BlockNode block) { public IfNode(IExpressionNode expression, BlockNode block) {
this.expression = expression; this.expression = expression;
this.block = block; this.block = block;
} }
@Override
public TypeCheckResult accept(SemanticVisitor visitor) {
return null;
}
} }

View File

@ -1,4 +0,0 @@
package ast.statements;
public class StatementNode {
}

View File

@ -2,23 +2,21 @@ package ast.statements;
import ast.ASTNode; import ast.ASTNode;
import ast.expressions.IExpressionNode; import ast.expressions.IExpressionNode;
import bytecode.visitor.MethodVisitor;
import semantic.SemanticVisitor; import semantic.SemanticVisitor;
import typechecker.TypeCheckResult; import typechecker.TypeCheckResult;
import visitor.Visitable;
public class WhileNode extends StatementNode implements ASTNode, Visitable { public class WhileNode implements IStatementNode {
public IExpressionNode expression; IExpressionNode expression;
public BlockNode block; BlockNode block;
public WhileNode(IExpressionNode expression, BlockNode block) { public WhileNode(IExpressionNode expression, BlockNode block) {
this.expression = expression; this.expression = expression;
this.block = block; this.block = block;
} }
@Override public void test() {
public void accept(MethodVisitor methodVisitor) { return;
methodVisitor.visit(this);
} }
@Override @Override

View File

@ -20,13 +20,11 @@ import java.nio.file.Paths;
* <p> <code> cd .\src\test\ </code> * <p> <code> cd .\src\test\ </code>
* <p> <code> make clean compile-raupenpiler </code> * <p> <code> make clean compile-raupenpiler </code>
* <p> Start Raupenpiler using jar: * <p> Start Raupenpiler using jar:
* <p> <code> java.exe -jar path_to_jar\JavaCompiler-1.0-SNAPSHOT-jar-with-dependencies.jar 'path_to_input_file.java' 'path_to_output_directory' </code> * <p> <code> java.exe -jar path_to_jar\JavaCompiler-1.0-jar-with-dependencies.jar 'path_to_input_file.java' 'path_to_output_directory' </code>
* <p> Example (jar needs to be in the target directory, compile with make or mvn package first): * <p> Example (jar needs to be in the target directory, compile with make or mvn package first):
* <code> java.exe -jar .\target\JavaCompiler-1.0-SNAPSHOT-jar-with-dependencies.jar 'src/main/resources/input/CompilerInput.java' 'src/main/resources/output' </code> * <code> java.exe -jar .\target\JavaCompiler-1.0-jar-with-dependencies.jar 'src/main/resources/input/CompilerInput.java' 'src/main/resources/output' </code>
*/ */
public class Main { public class Main {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
if (args.length == 2) { if (args.length == 2) {
// args[0] is the input file path // args[0] is the input file path

View File

@ -16,18 +16,18 @@ import java.util.List;
import java.util.logging.*; import java.util.logging.*;
/** /**
Beispiel für Logging-Arten: * Beispiel für Logging-Arten:
<p><code>logger.severe("Schwerwiegender Fehler");</code> * <p><code>logger.severe("Schwerwiegender Fehler");</code>
<p><code>logger.warning("Warnung");</code> * <p><code>logger.warning("Warnung");</code>
<p><code>logger.info("Information");</code> * <p><code>logger.info("Information");</code>
<p><code>logger.config("Konfigurationshinweis");</code> * <p><code>logger.config("Konfigurationshinweis");</code>
<p><code>logger.fine("Fein");</code> * <p><code>logger.fine("Fein");</code>
<p><code>logger.finer("Feiner");</code> * <p><code>logger.finer("Feiner");</code>
<p><code>logger.finest("Am feinsten");</code> * <p><code>logger.finest("Am feinsten");</code>
<p>You may toggle the logging level of the console and file handlers by * <p>You may toggle the logging level of the console and file handlers by
changing the level ALL/OFF/etc. in the constructor. * changing the level ALL/OFF/etc. in the constructor.
<code>consoleHandler.setLevel(Level.OFF);</code> * <code>consoleHandler.setLevel(Level.OFF);</code>
<code>fileHandler.setLevel(Level.ALL);</code> * <code>fileHandler.setLevel(Level.ALL);</code>
*/ */
public class RaupenLogger { public class RaupenLogger {
@ -172,9 +172,9 @@ public class RaupenLogger {
} }
String indentString = " ".repeat(indent * 2); String indentString = " ".repeat(indent * 2);
logger.info(indentString + abstractSyntaxTree.getClass()); logger.info(indentString + abstractSyntaxTree.getClass());
//for (ASTNode child : abstractSyntaxTree.getChildren()) {
// for (ASTNode child : node.) { // logAST(child, indent + 1);
// printAST(child, indent + 1);
// } // }
} }
} }

View File

@ -44,11 +44,28 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
@Override @Override
public ASTNode visitClassDeclaration(SimpleJavaParser.ClassDeclarationContext ctx) { public ASTNode visitClassDeclaration(SimpleJavaParser.ClassDeclarationContext ctx) {
ClassNode classNode = new ClassNode(ctx.AccessModifier().getText(), ctx.Identifier().getText()); ClassNode classNode;
for (SimpleJavaParser.MemberDeclarationContext member : ctx.memberDeclaration()) { if(ctx.AccessModifier() != null){
classNode.addMember((MemberNode) visit(member)); classNode = new ClassNode(ctx.AccessModifier().getText(), ctx.Identifier().getText());
} }
classNode.ensureConstructor(); else{
classNode = new ClassNode("public", ctx.Identifier().getText());
}
boolean hasConstructor = false;
for (SimpleJavaParser.MemberDeclarationContext member : ctx.memberDeclaration()) {
MemberNode memberNode = (MemberNode) visit(member);
if(memberNode instanceof ConstructorNode){
hasConstructor = true;
}
classNode.addMember(memberNode);
}
if(!hasConstructor){
classNode.members.addFirst(new ConstructorNode(new AccessModifierNode("public"), ctx.Identifier().getText()));
}
return classNode; return classNode;
} }
@ -60,13 +77,16 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
constructorNode.addParameter((ParameterNode) visit(parameter)); constructorNode.addParameter((ParameterNode) visit(parameter));
} }
} }
constructorNode.block.addStatement(new ReturnNode(null));
return constructorNode; return constructorNode;
} }
@Override @Override
public ASTNode visitMethodDeclaration(SimpleJavaParser.MethodDeclarationContext ctx) { public ASTNode visitMethodDeclaration(SimpleJavaParser.MethodDeclarationContext ctx) {
if(ctx.MainMethodDeclaration() != null) { if(ctx.MainMethodDeclaration() != null) {
return new MainMethodNode((BlockNode) visit(ctx.blockStatement())); MainMethodNode mainMethod = new MainMethodNode((BlockNode) visit(ctx.blockStatement()));
mainMethod.block.addStatement(new ReturnNode(null));
return mainMethod;
} else { } else {
if(ctx.type() != null) { if(ctx.type() != null) {
MethodNode methodNode = new MethodNode(ctx.AccessModifier().getText(), createTypeNode(ctx.type().getText()), false, ctx.Identifier().getText(), (BlockNode) visit(ctx.blockStatement())); MethodNode methodNode = new MethodNode(ctx.AccessModifier().getText(), createTypeNode(ctx.type().getText()), false, ctx.Identifier().getText(), (BlockNode) visit(ctx.blockStatement()));
@ -83,6 +103,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
methodNode.addParameter((ParameterNode) visit(parameter)); methodNode.addParameter((ParameterNode) visit(parameter));
} }
} }
methodNode.block.addStatement(new ReturnNode(null));
return methodNode; return methodNode;
} }
} }
@ -108,6 +129,8 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
return visitBlockStatement(ctx.blockStatement()); return visitBlockStatement(ctx.blockStatement());
} else if(ctx.whileStatement() != null) { } else if(ctx.whileStatement() != null) {
return visitWhileStatement(ctx.whileStatement()); return visitWhileStatement(ctx.whileStatement());
} else if(ctx.doWhileStatement() != null) {
return visitDoWhileStatement(ctx.doWhileStatement());
} else if(ctx.forStatement() != null) { } else if(ctx.forStatement() != null) {
return visitForStatement(ctx.forStatement()); return visitForStatement(ctx.forStatement());
} else if(ctx.ifElseStatement() != null) { } else if(ctx.ifElseStatement() != null) {
@ -134,9 +157,6 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
for(SimpleJavaParser.StatementContext statement : ctx.statement()) { for(SimpleJavaParser.StatementContext statement : ctx.statement()) {
blockNode.addStatement((IStatementNode) visit(statement)); blockNode.addStatement((IStatementNode) visit(statement));
} }
if(!blockNode.hasReturnStatement) {
blockNode.addStatement(new ReturnNode(null));
}
return blockNode; return blockNode;
} }
@ -161,28 +181,30 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
@Override @Override
public ASTNode visitForStatement(SimpleJavaParser.ForStatementContext ctx) { public ASTNode visitForStatement(SimpleJavaParser.ForStatementContext ctx) {
List<StatementNode> statements = new ArrayList<>(); List<IStatementNode> statements = new ArrayList<>();
//init //init
if(ctx.statementExpression(0) != null){ int i = 0;
statements.add((StatementNode) visit(ctx.statementExpression(0))); if(ctx.localVariableDeclaration() != null) {
} else if (ctx.localVariableDeclaration() != null) { statements.add((IStatementNode) visit(ctx.localVariableDeclaration()));
statements.add((StatementNode) visit(ctx.localVariableDeclaration())); } else if(ctx.statementExpression(i) != null){
statements.add((IStatementNode) visit(ctx.statementExpression(i)));
i++;
} }
//condition //condition
IExpressionNode condition = (IExpressionNode) visit(ctx.expression()); IExpressionNode condition = (IExpressionNode) visit(ctx.expression());
//ink //ink
IStatementNode increment = null; IStatementNode crement = null;
if(ctx.statementExpression(1) != null){ if(ctx.statementExpression(i) != null){
increment = (IStatementNode) visit(ctx.statementExpression(1)); crement = (IStatementNode) visit(ctx.statementExpression(i));
} }
BlockNode forBlock = (BlockNode) visit(ctx.blockStatement()); BlockNode forBlock = (BlockNode) visit(ctx.blockStatement());
if(increment != null){ if(crement != null){
forBlock.addStatement((increment)); forBlock.addStatement((crement));
} }
WhileNode While = new WhileNode(condition, forBlock); WhileNode While = new WhileNode(condition, forBlock);
@ -190,7 +212,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor<ASTNode> {
statements.add(While); statements.add(While);
BlockNode resultBlock = new BlockNode(); BlockNode resultBlock = new BlockNode();
for(StatementNode statement : statements) { for(IStatementNode statement : statements) {
resultBlock.addStatement((IStatementNode) statement); resultBlock.addStatement((IStatementNode) statement);
} }

View File

@ -1,4 +1,4 @@
// Generated from C:/Users/Maxi/Documents/DHBW/Compilerbau/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1 // Generated from C:/Users/janni/Desktop/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1
package parser.generated; package parser.generated;
import org.antlr.v4.runtime.ParserRuleContext; import org.antlr.v4.runtime.ParserRuleContext;

View File

@ -1,4 +1,4 @@
// Generated from C:/Users/Maxi/Documents/DHBW/Compilerbau/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1 // Generated from C:/Users/janni/Desktop/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1
package parser.generated; package parser.generated;
import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor; import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;

View File

@ -1,4 +1,4 @@
// Generated from C:/Users/Maxi/Documents/DHBW/Compilerbau/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1 // Generated from C:/Users/janni/Desktop/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1
package parser.generated; package parser.generated;
import org.antlr.v4.runtime.Lexer; import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStream;

View File

@ -1,4 +1,4 @@
// Generated from C:/Users/Maxi/Documents/DHBW/Compilerbau/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1 // Generated from C:/Users/janni/Desktop/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1
package parser.generated; package parser.generated;
import org.antlr.v4.runtime.tree.ParseTreeListener; import org.antlr.v4.runtime.tree.ParseTreeListener;

View File

@ -1,4 +1,4 @@
// Generated from C:/Users/Maxi/Documents/DHBW/Compilerbau/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1 // Generated from C:/Users/janni/Desktop/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1
package parser.generated; package parser.generated;
import org.antlr.v4.runtime.atn.*; import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA; import org.antlr.v4.runtime.dfa.DFA;

View File

@ -1,4 +1,4 @@
// Generated from C:/Users/Maxi/Documents/DHBW/Compilerbau/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1 // Generated from C:/Users/janni/Desktop/NichtHaskell2.0/src/main/java/parser/grammar/SimpleJava.g4 by ANTLR 4.13.1
package parser.generated; package parser.generated;
import org.antlr.v4.runtime.tree.ParseTreeVisitor; import org.antlr.v4.runtime.tree.ParseTreeVisitor;

View File

@ -1,7 +1,7 @@
package semantic; package semantic;
import ast.type.type.*; import ast.type.type.*;
import semantic.exeptions.AlreadyDeclearedException; import semantic.exceptions.AlreadyDeclaredException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Stack; import java.util.Stack;
@ -16,7 +16,7 @@ public class Scope {
public void addLocalVar(String name, ITypeNode type) { public void addLocalVar(String name, ITypeNode type) {
if (this.contains(name)) { if (this.contains(name)) {
throw new AlreadyDeclearedException("Variable " + name + " already exists in this scope"); throw new AlreadyDeclaredException("Variable " + name + " already exists in this scope");
} }
localVars.peek().put(name, type); localVars.peek().put(name, type);
} }

View File

@ -24,7 +24,7 @@ import ast.statementexpressions.methodcallstatementnexpressions.MethodCallNode;
import ast.statements.*; import ast.statements.*;
import ast.type.type.*; import ast.type.type.*;
import semantic.context.Context; import semantic.context.Context;
import semantic.exeptions.*; import semantic.exceptions.*;
import typechecker.TypeCheckResult; import typechecker.TypeCheckResult;
public class SemanticAnalyzer implements SemanticVisitor { public class SemanticAnalyzer implements SemanticVisitor {
@ -116,7 +116,7 @@ public class SemanticAnalyzer implements SemanticVisitor {
if (Objects.equals(otherMethod, methodNode)) if (Objects.equals(otherMethod, methodNode))
break; break;
if (otherMethod.isSame(methodNode)) { if (otherMethod.isSame(methodNode)) {
errors.add(new AlreadyDeclearedException( errors.add(new AlreadyDeclaredException(
"Method " + methodNode.getIdentifier() + " is already defined in class " "Method " + methodNode.getIdentifier() + " is already defined in class "
+ currentClass.identifier)); + currentClass.identifier));
valid = false; valid = false;
@ -129,8 +129,8 @@ public class SemanticAnalyzer implements SemanticVisitor {
valid = valid && result.isValid(); valid = valid && result.isValid();
try { try {
currentScope.addLocalVar(parameter.identifier, parameter.type); currentScope.addLocalVar(parameter.identifier, parameter.type);
} catch (AlreadyDeclearedException e) { } catch (AlreadyDeclaredException e) {
errors.add(new AlreadyDeclearedException(parameter.identifier)); errors.add(new AlreadyDeclaredException(parameter.identifier));
} }
} }
@ -164,7 +164,7 @@ public class SemanticAnalyzer implements SemanticVisitor {
@Override @Override
public TypeCheckResult analyze(FieldNode toCheck) { public TypeCheckResult analyze(FieldNode toCheck) {
if (currentFields.get(toCheck.identifier) != null) { if (currentFields.get(toCheck.identifier) != null) {
errors.add(new AlreadyDeclearedException("Already declared " + toCheck.identifier)); errors.add(new AlreadyDeclaredException("Already declared " + toCheck.identifier));
return new TypeCheckResult(false, null); return new TypeCheckResult(false, null);
} else { } else {
currentFields.put(toCheck.identifier, toCheck.type); currentFields.put(toCheck.identifier, toCheck.type);
@ -361,7 +361,7 @@ public class SemanticAnalyzer implements SemanticVisitor {
} else if(currentFields.get(unary.identifier) != null) { } else if(currentFields.get(unary.identifier) != null) {
return new TypeCheckResult(valid, currentFields.get(unary.identifier)); return new TypeCheckResult(valid, currentFields.get(unary.identifier));
} else { } else {
errors.add(new NotDeclearedException("Var is not Decleared")); errors.add(new NotDeclaredException("Var is not Declared"));
} }
return new TypeCheckResult(valid, null); return new TypeCheckResult(valid, null);
} }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
package semantic.exeptions; package semantic.exceptions;
public class TypeMismatchException extends RuntimeException { 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,9 +0,0 @@
package semantic.exeptions;
public class AlreadyDeclearedException extends RuntimeException {
public AlreadyDeclearedException(String message) {
super(message);
}
}

View File

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

View File

@ -1,16 +1,14 @@
public class CompilerInput { public class Compiler {
public int add(int i, int j) {
public int a; return i+j;
}
public static int testMethod(char x){ }
return 0;
} public class Node {
public void main() {
public class Test { Compiler compiler = new Compiler();
int i = compiler.add(5, 8);
public static int testMethod(char x, int a){ return i;
return 0;
}
} }
} }

View File

@ -10,20 +10,33 @@ compile-javac:
compile-raupenpiler: compile-raupenpiler:
cd ../.. ; mvn -DskipTests install cd ../.. ; mvn -DskipTests install
cd ../.. ; mvn exec:java -Dexec.mainClass="main.Main" -Dexec.args="'src/main/resources/input/CompilerInput.java' 'src/main/resources/output' " cd ../.. ; mvn exec:java -Dexec.mainClass="main.Main" -Dexec.args="'src/main/resources/input/CompilerInput.java' 'src/main/resources/output' "
cp ../main/resources/output/CompilerInput.class .java/resources/output/raupenpiler
test: test-javac test-raupenpiler test: compile-javac compile-raupenpiler test-javac test-raupenpiler
test-javac: test-javac:
#compile-javac # gleich wie bei raupenpiler, kann ich ohne funktionierenden Compiler nicht testen
#java -cp .\resources\output\javac CompilerInput
test-raupenpiler: test-raupenpiler:
#java -cp .\resources\output\raupenpiler CompilerInput # move the compiled class to the test/main folder
mv ../main/resources/output/CompilerInput.class .java/main/
# compile the test class
javac .java/main.EndToEndTester.java
# run the test class
java .java/main.EndToEndTester
clean: clean:
# clean output folders
rm -f ../main/resources/output/*.class
rm -f ./resources/output/javac/*.class rm -f ./resources/output/javac/*.class
rm -f ./resources/output/raupenpiler/*.class rm -f ./resources/output/raupenpiler/*.class
rm -f ./java/*.class # clean logs
rm -f ../main/resources/output/*.class
rm -f ../main/resources/logs/*.log rm -f ../main/resources/logs/*.log
# clean test/main folders from .class files for End-to-End tests
rm -f ./java/main/*.class
# clean javac output from featureTests
rm -f ./resources/input/featureTests/*.class

View File

@ -78,8 +78,8 @@ Compiled Classfile
wenn beides erfolgreich wenn beides erfolgreich
- Ergebnis vom eigenen Compiler mithilfe von main.TestCompilerOutput ausführen - Ergebnis vom eigenen Compiler mithilfe von main.EndToEndTester ausführen
- (Ergebnis von javac mithilfe von main.TestCompilerOutput ausführen) - (Ergebnis von javac mithilfe von main.EndToEndTester ausführen)
### Andis Tipps: ### Andis Tipps:
@ -89,4 +89,5 @@ wenn beides erfolgreich
- mvn package - mvn package
- javac tester // tester compilen - javac tester // tester compilen
- java tester // tester ausführen - java tester // tester ausführen
- -> tester ist in unserem Fall main.TestCompilerOutput.java - -> tester ist in unserem Fall main.EndToEndTester.java
- -> Hab ich alles umgesetzt

View File

@ -1,6 +0,0 @@
package main;
public class EmptyClassExample {
private class Inner {
}
} // -o für outout

View File

@ -3,18 +3,18 @@ package main;
/** /**
* This class is used to test the output of the compiler. * This class is used to test the output of the compiler.
* *
* <p>Im gleichen Ordner wie diese Datei (main.TestCompilerOutput.java) muss die selbst kompilierte CompilerInput.class Datei sein. * <p>Im gleichen Ordner wie diese Datei (EndToEndTester.java) muss die selbst kompilierte CompilerInput.class Datei sein.
* <br><strong>Hinweis:</strong> Diese muss man also vom Ordner <code> main/resources/output </code> in diesen Ordner hier (test/java) rein kopieren. (bis es eine bessere Lösung gibt)</p> * <br><strong>Hinweis:</strong> Diese muss man also vom Ordner <code> main/resources/output </code> in diesen Ordner hier (test/java/main) rein kopieren. (bis es eine bessere Lösung gibt -> bin grad in der Make dran das alles hier automatisch zu machen)</p>
* *
* <p>Die selbst kompilierte .class Datei wird dann hier drin geladen und eine Instanz von ihr erstellt, es können auch Methoden aufgerufen werden. * <p>Die selbst kompilierte .class Datei wird dann hier drin geladen und eine Instanz von ihr erstellt, es können auch Methoden aufgerufen werden.
* <p>Diese main.TestCompilerOutput.java Datei wird dann in <code> \src\test\java> </code> mit <code>javac .\main.TestCompilerOutput.java</code> kompiliert und mit <code>java main.TestCompilerOutput</code> ausgeführt. * <p>Diese EndToEndTester.java Datei wird dann in <code> \src\test\java> </code> mit <code>javac .\main.EndToEndTester.java</code> kompiliert und mit <code>java main.EndToEndTester</code> ausgeführt.
* Wenn unser Compiler funktioniert, sollten keine Errors kommen (sondern nur die Ausgaben, die wir in der CompilerInput.java Datei gemacht haben, * Wenn unser Compiler funktioniert, sollten keine Errors kommen (sondern nur die Ausgaben, die wir in der CompilerInput.java Datei gemacht haben,
* oder Methoden, die wir hier aufrufen).</p> * oder Methoden, die wir hier aufrufen).</p>
* *
* <p><strong>PROBLEM:</strong> Hier kommen Errors, was eigentlich heißt, dass der Compiler nicht funktioniert, der Test sollte eigentlich passen. * <p><strong>PROBLEM:</strong> Hier kommen Errors, was eigentlich heißt, dass der Compiler nicht funktioniert, der Test sollte eigentlich passen.
* <br><strong>DENN:</strong> Wenn ich statt unserem CompilerInput.class die CompilerInput.class von javac verwende (aus <code> src/test/resources/output/javac </code>), dann funktioniert es.</p> * <br><strong>DENN:</strong> Wenn ich statt unserem CompilerInput.class die CompilerInput.class von javac verwende (aus <code> src/test/resources/output/javac </code>), dann funktioniert es.</p>
*/ */
public class TestCompilerOutput { public class EndToEndTester {
public static void main(String[] args) { public static void main(String[] args) {
try { try {
// Try to load the class named "CompilerInput" // Try to load the class named "CompilerInput"

View File

@ -3,71 +3,45 @@ package main;
import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertTrue;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import javax.tools.JavaCompiler; import javax.tools.JavaCompiler;
import javax.tools.ToolProvider; import javax.tools.ToolProvider;
import java.io.File; import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;
public class FailureTest { public class FailureTest {
private static final List<String> TEST_FILES = Arrays.asList(
"src/main/test/resources/input/failureTests/TestClass1.java",
"src/main/test/resources/input/failureTests/TestClass2.java",
"src/main/test/resources/input/failureTests/TestClass3.java",
"src/main/test/resources/input/failureTests/TestClass4.java",
"src/main/test/resources/input/failureTests/TestClass5.java",
"src/main/test/resources/input/failureTests/TestClass6.java",
"src/main/test/resources/input/failureTests/TestClass7.java",
"src/main/test/resources/input/failureTests/TestClass8.java",
"src/main/test/resources/input/failureTests/TestClass9.java",
"src/main/test/resources/input/failureTests/TestClass10.java",
"src/main/test/resources/input/failureTests/TestClass11.java"
);
/** /**
* This test method checks if invalid Java files fail to compile as expected. * This test method checks if invalid Java files fail to compile as expected.
* It uses the JavaCompiler from the ToolProvider to compile the files. * It uses the JavaCompiler from the ToolProvider to compile the files.
* The test passes if all the files fail to compile. * The test passes if all the files fail to compile.
*/ */
@Test @Test
public void invalidJavaFilesTest() { public void areTestFilesActuallyFailTest() {
// Get the system Java compiler // Get the system Java compiler
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
// Assert that the compiler is available // Assert that the compiler is available
assertNotNull(compiler, "Java Compiler is not available"); assertNotNull(javac, "Java Compiler is not available");
// Iterate over the test files String directoryPath = "src/test/resources/input/failureTests";
for (String fileName : TEST_FILES) { File folder = new File(directoryPath);
// Create a File object for the current file
File file = new File(fileName);
if (folder.isDirectory()) {
File[] files = folder.listFiles((dir, name) -> name.endsWith(".java"));
if (files != null) {
for (File file : files) {
// Try to compile the file and get the result // Try to compile the file and get the result
// The run method returns 0 if the compilation was successful, and non-zero otherwise // The run method returns 0 if the compilation was successful, and non-zero otherwise
int result = compiler.run(null, null, null, file.getPath()); int result = javac.run(null, null, null, file.getPath());
// Assert that the compilation failed (i.e., the result is non-zero) // Assert that the compilation failed (i.e., the result is non-zero)
assertTrue(result != 0, "Expected compilation failure for " + fileName); assertTrue(result != 0, "Expected compilation failure for " + file.getName());
} }
} else {
System.out.println("No files found in the directory.");
} }
} else {
// schmeißt John Fehler, wenn namen doppelt sind? System.out.println("The provided path is not a directory.");
// Input: ParseTree mit genanntem Fehler
// Output: Fehlermeldung
@Test
void typedASTTest() throws IOException {
CharStream codeCharStream = null;
try {
codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/main/EmptyClassExample.java"));
Main.compileFile(codeCharStream, "src/main/test/resources/output");
} catch (IOException e) {
System.err.println("Error reading the file: " + e.getMessage());
} }
} }
} }

View File

@ -0,0 +1,46 @@
package main;
import org.junit.jupiter.api.Test;
import javax.tools.JavaCompiler;
import javax.tools.ToolProvider;
import java.io.File;
import static org.junit.jupiter.api.Assertions.*;
public class FeatureTest {
/**
* This test method checks if valid Java files compile successfully.
* It uses the JavaCompiler from the ToolProvider to compile the files.
* The test passes if all the files compile without errors.
*/
@Test
public void areTestFilesActuallyValid() {
// Get the system Java compiler
JavaCompiler javac = ToolProvider.getSystemJavaCompiler();
// Assert that the compiler is available
assertNotNull(javac, "Java Compiler is not available");
String directoryPath = "src/test/resources/input/featureTests";
File folder = new File(directoryPath);
if (folder.isDirectory()) {
File[] files = folder.listFiles((dir, name) -> name.endsWith(".java"));
if (files != null) {
for (File file : files) {
// Try to compile the file and get the result
// The run method returns 0 if the compilation was successful, and non-zero otherwise
int result = javac.run(null, null, null, file.getPath());
// Assert that the compilation succeeded (i.e., the result is zero)
assertEquals(0, result, "Expected compilation success for " + file.getName());
}
} else {
System.out.println("No files found in the directory.");
}
} else {
System.out.println("The provided path is not a directory.");
}
}
}

View File

@ -7,14 +7,14 @@ import org.antlr.v4.runtime.CharStreams;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Paths; import java.nio.file.Paths;
import static org.junit.jupiter.api.Assertions.assertEquals;
/** /**
* run: mvn test * run every test: mvn test
* Nutzen dieser Klasse: Eigentlich nicht vorhanden, in der Main gibts nichts zu testen
*/ */
public class MainTest { public class MainTest {
@Test @Test
void testEmptyClass() { void test() {
CharStream codeCharStream = null; CharStream codeCharStream = null;
try { try {
codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/CompilerInput.java")); codeCharStream = CharStreams.fromPath(Paths.get("src/main/test/resources/CompilerInput.java"));

View File

@ -0,0 +1,51 @@
package parser;
import ast.ASTNode;
import ast.ClassNode;
import ast.ProgramNode;
import ast.members.ConstructorNode;
import ast.members.MemberNode;
import ast.type.AccessModifierNode;
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.DisplayName;
import org.junit.jupiter.api.Test;
import parser.astBuilder.ASTBuilder;
import parser.generated.SimpleJavaLexer;
import parser.generated.SimpleJavaParser;
import java.io.IOException;
import static org.assertj.core.api.Assertions.assertThat;
@DisplayName("Untyped Abstract Syntax Tree")
class AstBuilderTest {
@Test
public void emptyClassTest(){
MemberNode constructor = new ConstructorNode(new AccessModifierNode("public"),"EmptyClass");
ClassNode emptyClass = new ClassNode("public", "EmptyClass");
emptyClass.addMember(constructor);
var expected = new ProgramNode();
expected.addClass(emptyClass);
CharStream testFile = null;
try {
testFile = CharStreams.fromFileName("src/test/resources/input/javaCases/EmptyClass.java");
} catch (IOException e) {
throw new RuntimeException(e);
}
SimpleJavaLexer lexer = new SimpleJavaLexer(testFile);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
tokenStream.fill();
SimpleJavaParser parser = new SimpleJavaParser(tokenStream);
ParseTree parseTree = parser.program();
ASTBuilder astBuilder = new ASTBuilder();
var actual = astBuilder.visit(parseTree);
assertThat(actual).isEqualToComparingFieldByFieldRecursively(expected);
}
}

View File

@ -4,7 +4,6 @@ import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.tree.ParseTree; import org.antlr.v4.runtime.tree.ParseTree;
import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import parser.astBuilder.ASTBuilder;
import parser.generated.SimpleJavaLexer; import parser.generated.SimpleJavaLexer;
import parser.generated.SimpleJavaParser; import parser.generated.SimpleJavaParser;
@ -13,41 +12,13 @@ import static org.junit.jupiter.api.Assertions.*;
import java.util.*; import java.util.*;
public class ParserTest { public class ParserTest {
/*
@BeforeEach @BeforeEach
public void init() { // noch nicht benötigt public void init() { // noch nicht benötigt
String inputFilePath = "src/main/resources/input/CompilerInput.java"; String inputFilePath = "src/main/resources/input/CompilerInput.java";
String outputDirectoryPath = "src/main/resources/output"; String outputDirectoryPath = "src/main/resources/output";
} }
/**
* This test method is used to test the scanner functionality of the SimpleJavaLexer.
* It creates a CharStream from a string representing a simple Java class declaration,
* and uses the SimpleJavaLexer to tokenize this input.
* It then compares the actual tokens and their types produced by the lexer to the expected tokens and their types.
*/ */
@Test
public void scannerTest() {
// Create a CharStream from a string representing a simple Java class declaration
CharStream inputCharStream = CharStreams.fromString("public class Name {}");
// Use the SimpleJavaLexer to tokenize the input
SimpleJavaLexer lexer = new SimpleJavaLexer(inputCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
tokenStream.fill();
// Prepare the expected results
List<Token> actualTokens = tokenStream.getTokens();
List<String> expectedTokens = Arrays.asList("public", "class", "Name", "{", "}", "<EOF>");
List<String> expectedTokenTypes = Arrays.asList(null, null, "IDENTIFIER", null, null, "EOF");
// Compare the actual tokens and their types to the expected tokens and their types
assertEquals(expectedTokens.size(), actualTokens.size());
for (int i = 0; i < expectedTokens.size(); i++) {
assertEquals(expectedTokens.get(i), actualTokens.get(i).getText());
assertEquals(expectedTokenTypes.get(i), SimpleJavaLexer.VOCABULARY.getSymbolicName(actualTokens.get(i).getType()));
}
}
@Test @Test
public void parserTest() { public void parserTest() {
@ -63,38 +34,20 @@ public class ParserTest {
ParseTree parseTree = parser.program(); // parse the input ParseTree parseTree = parser.program(); // parse the input
//Variante 1 (geht) //Variante 1 (geht)
String expectedParseTreeAsString = "(program (classDeclaration public class Name { }))";
String actualParseTreeAsString = parseTree.toStringTree(parser); String actualParseTreeAsString = parseTree.toStringTree(parser);
String expectedParseTreeAsString = "(program (classDeclaration (accessType public) class Name { }))";
assertEquals(actualParseTreeAsString, expectedParseTreeAsString); assertEquals(expectedParseTreeAsString, actualParseTreeAsString);
//Variante 2 (geht nicht) // Variante 2 (geht nicht)
// - Sollte es gehen und es liegt am Parser? (keine Ahnung) -> Bitte Fehler (actual und expected) durchlesen // - Sollte es gehen und es liegt am Parser? (keine Ahnung) -> Bitte Fehler (actual und expected) durchlesen
Map<String, Object> actualTreeStructure = buildTreeStructure(parseTree, parser); // ist die Methode parseStringToTree() korrekt? -> (glaub nicht)
Map<String, Object> expectedTreeStructure = parseStringToTree(expectedParseTreeAsString); Map<String, Object> expectedTreeStructure = parseStringToTree(expectedParseTreeAsString);
Map<String, Object> actualTreeStructure = buildTreeStructure(parseTree, parser);
assertEquals(actualTreeStructure, expectedTreeStructure); // assertEquals(expectedTreeStructure, actualTreeStructure);
} }
@Test
public void astBuilderTest() {
// TODO: Implement this test method
/* AST builder -> AST */
ASTBuilder astBuilder = new ASTBuilder();
// ProgramNode abstractSyntaxTree = (ProgramNode) astBuilder.visit(parseTree);
//String actualASTasString = new ASTBuilder().visit(parseTree).toString();
// ProgramNode actualAST = new ASTBuilder().visit(parseTree);
// ProgramNode expectedAST = new ProgramNode();
// expectedAST.add(new ProgramNode.ClassNode("Name", new ProgramNode()));
}
// Helpers Variante 2.1 // Helpers Variante 2.1
@ -146,7 +99,7 @@ public class ParserTest {
for (char ch : input.toCharArray()) { for (char ch : input.toCharArray()) {
if (ch == '(') { if (ch == '(') {
if (depth == 0) { if (depth == 0) {
if (currentToken.length() > 0) { if (!currentToken.isEmpty()) {
node.put("node", currentToken.toString().trim()); node.put("node", currentToken.toString().trim());
currentToken.setLength(0); currentToken.setLength(0);
} }
@ -163,7 +116,7 @@ public class ParserTest {
currentToken.append(ch); currentToken.append(ch);
} }
} else if (Character.isWhitespace(ch) && depth == 0) { } else if (Character.isWhitespace(ch) && depth == 0) {
if (currentToken.length() > 0) { if (!currentToken.isEmpty()) {
node.put("node", currentToken.toString().trim()); node.put("node", currentToken.toString().trim());
currentToken.setLength(0); currentToken.setLength(0);
} }
@ -172,7 +125,7 @@ public class ParserTest {
} }
} }
if (currentToken.length() > 0) { if (!currentToken.isEmpty()) {
node.put("node", currentToken.toString().trim()); node.put("node", currentToken.toString().trim());
} }

View File

@ -0,0 +1,45 @@
package parser;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.Token;
import org.junit.jupiter.api.Test;
import parser.generated.SimpleJavaLexer;
import java.util.Arrays;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class ScannerTest {
/**
* This test method is used to test the scanner functionality of the SimpleJavaLexer.
* It creates a CharStream from a string representing a simple Java class declaration,
* and uses the SimpleJavaLexer to tokenize this input.
* It then compares the actual tokens and their types produced by the lexer to the expected tokens and their types.
*/
@Test
public void scannerTest() {
// Create a CharStream from a string representing a simple Java class declaration
CharStream inputCharStream = CharStreams.fromString("public class Name {}");
// Use the SimpleJavaLexer to tokenize the input
SimpleJavaLexer lexer = new SimpleJavaLexer(inputCharStream);
CommonTokenStream tokenStream = new CommonTokenStream(lexer);
tokenStream.fill();
// Prepare the expected results
List<String> expectedTokens = Arrays.asList("public", "class", "Name", "{", "}", "<EOF>");
List<String> expectedTokenTypes = Arrays.asList("AccessModifier", "Class", "Identifier", "OpenCurlyBracket", "ClosedCurlyBracket", "EOF");
List<Token> actualTokens = tokenStream.getTokens();
// Compare the actual tokens and their types to the expected tokens and their types
assertEquals(expectedTokens.size(), actualTokens.size());
for (int i = 0; i < expectedTokens.size(); i++) {
assertEquals(expectedTokens.get(i), actualTokens.get(i).getText());
assertEquals(expectedTokenTypes.get(i), SimpleJavaLexer.VOCABULARY.getSymbolicName(actualTokens.get(i).getType()));
}
}
}

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; package semantic;
import ast.*;
import ast.members.FieldNode;
import ast.members.MemberNode;
import ast.members.MethodNode;
import ast.parameters.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 { public class SemanticTest {
@ -35,7 +22,7 @@ public class SemanticTest {
// ASTNode typedAst = SemanticAnalyzer.generateTast(programNode); // ASTNode typedAst = SemanticAnalyzer.generateTast(programNode);
// //
// assertEquals(1, SemanticAnalyzer.errors.size()); // assertEquals(1, SemanticAnalyzer.errors.size());
// assertInstanceOf(AlreadyDeclearedException.class, SemanticAnalyzer.errors.getFirst()); // assertInstanceOf(AlreadyDeclaredException.class, SemanticAnalyzer.errors.getFirst());
// assertNull(typedAst); // assertNull(typedAst);
// } // }
// //

View File

@ -1,5 +1,3 @@
package resources;
public class AllFeaturesClassExample { public class AllFeaturesClassExample {
int a; int a;
boolean b; boolean b;

View File

@ -1,5 +1,3 @@
package resources;
public class CombinedExample { public class CombinedExample {
int number; int number;
boolean flag; boolean flag;

View File

@ -1,5 +1,3 @@
package resources;
public class MoreFeaturesClassExample { public class MoreFeaturesClassExample {
int hallo; int hallo;
private class Inner { private class Inner {

View File

@ -1,5 +1,3 @@
package resources.featureTests;
public class BooleanOperations { public class BooleanOperations {
boolean flag; boolean flag;

View File

@ -1,5 +1,3 @@
package resources.featureTests;
public class CharManipulation { public class CharManipulation {
char letter; char letter;

View File

@ -1,5 +1,3 @@
package resources.featureTests;
public class ConditionalStatements { public class ConditionalStatements {
int number; int number;

View File

@ -0,0 +1,2 @@
public class EmptyClassExample {
}

View File

@ -1,5 +1,3 @@
package resources.featureTests;
public class LoopExamples { public class LoopExamples {
public static void main(String[] args) { public static void main(String[] args) {
// For loop example // For loop example

View File

@ -1,5 +1,3 @@
package resources.featureTests;
public class MethodOverloading { public class MethodOverloading {
public int add(int a, int b) { public int add(int a, int b) {
return a + b; return a + b;

View File

@ -0,0 +1 @@
class EmptyClass { }

View File

@ -0,0 +1,7 @@
class EmptyClass1{
}
class EmptyClass2{
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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