diff --git a/src/main/java/ast/block/BlockNode.java b/src/main/java/ast/block/BlockNode.java index a5ff3f7..67d9e81 100644 --- a/src/main/java/ast/block/BlockNode.java +++ b/src/main/java/ast/block/BlockNode.java @@ -2,11 +2,14 @@ package ast.block; import ast.ASTNode; import ast.statement.StatementNode; +import semantic.SemanticVisitor; +import typechecker.TypeCheckResult; +import visitor.Visitable; import java.util.ArrayList; import java.util.List; -public class BlockNode implements ASTNode { +public class BlockNode implements ASTNode, Visitable { public List statements = new ArrayList<>(); public BlockNode() {} @@ -14,4 +17,10 @@ public class BlockNode implements ASTNode { public void addStatement(StatementNode statement) { statements.add(statement); } + + @Override + public TypeCheckResult accept(SemanticVisitor visitor) { + return visitor.analyze(this); + } + } diff --git a/src/main/java/ast/expression/ExpressionNode.java b/src/main/java/ast/expression/ExpressionNode.java index 1566f3b..282b9b7 100644 --- a/src/main/java/ast/expression/ExpressionNode.java +++ b/src/main/java/ast/expression/ExpressionNode.java @@ -1,4 +1,6 @@ package ast.expression; -public class ExpressionNode { +import ast.ASTNode; + +public class ExpressionNode implements ASTNode { } diff --git a/src/main/java/ast/expression/unaryexpression/UnaryExpressionNode.java b/src/main/java/ast/expression/unaryexpression/UnaryExpressionNode.java index fdb694a..38c014d 100644 --- a/src/main/java/ast/expression/unaryexpression/UnaryExpressionNode.java +++ b/src/main/java/ast/expression/unaryexpression/UnaryExpressionNode.java @@ -7,7 +7,7 @@ import ast.type.ValueNode; import java.util.Objects; -public class UnaryExpressionNode implements ASTNode { +public class UnaryExpressionNode extends ExpressionNode { String thisExp; String identifier; MemberAccessNode memberAccess; diff --git a/src/main/java/ast/member/MethodNode.java b/src/main/java/ast/member/MethodNode.java index 2d79ee5..bedfac2 100644 --- a/src/main/java/ast/member/MethodNode.java +++ b/src/main/java/ast/member/MethodNode.java @@ -9,14 +9,15 @@ import semantic.SemanticVisitor; import typechecker.TypeCheckResult; import visitor.Visitable; +import java.util.ArrayList; import java.util.List; public class MethodNode implements MemberNode, Visitable { AccessModifierNode accesModifier; - TypeNode type; + public TypeNode type; Boolean voidType; - String identifier; - List parameters; + public String identifier; + private List parameters = new ArrayList<>(); public BlockNode block; public MethodNode() {} @@ -37,24 +38,23 @@ public class MethodNode implements MemberNode, Visitable { this.parameters.add(parameter); } - /* - public boolean isSame(MethodNode methodNode){ - boolean isSame = false; - if(methodNode.identifier.equals(identifier)){ - if(parameters != null && methodNode.parameters != null){ - if(parameters.parameters.size() == methodNode.parameters.parameters.size()){ - for(int i = 0; i < parameters.parameters.size(); i++){ - if(parameters.parameters.get(i).identifier.equals(methodNode.parameters.parameters.get(i).identifier)){ - isSame = true; - } - } - } - } - } - return isSame; + public List getParameters() { + return parameters; } - */ + public boolean isSame(MethodNode methodNode){ + if (!this.identifier.equals(methodNode.identifier) || this.type.equals(methodNode.type) + || this.getParameters().size() != methodNode.getParameters().size()) { + return false; + } + + for (int i = 0; i < this.getParameters().size(); i++) { + if (this.getParameters().get(i).type.equals(methodNode.getParameters().get(i).type)) { + return false; + } + } + return true; + } @Override public TypeCheckResult accept(SemanticVisitor visitor) { diff --git a/src/main/java/ast/parameter/ParameterNode.java b/src/main/java/ast/parameter/ParameterNode.java index 3a6b5ec..9050f30 100644 --- a/src/main/java/ast/parameter/ParameterNode.java +++ b/src/main/java/ast/parameter/ParameterNode.java @@ -2,13 +2,22 @@ package ast.parameter; import ast.ASTNode; import ast.type.TypeNode; +import semantic.SemanticVisitor; +import typechecker.TypeCheckResult; +import visitor.Visitable; -public class ParameterNode implements ASTNode { - TypeNode type; - String identifier; +public class ParameterNode implements ASTNode, Visitable { + public TypeNode type; + public String identifier; public ParameterNode(TypeNode type, String identifier) { this.type = type; this.identifier = identifier; } + + @Override + public TypeCheckResult accept(SemanticVisitor visitor) { + return visitor.analyze(this); + } + } diff --git a/src/main/java/ast/statement/LocalVariableDeclarationNode.java b/src/main/java/ast/statement/LocalVariableDeclarationNode.java index 5b3900f..edee12b 100644 --- a/src/main/java/ast/statement/LocalVariableDeclarationNode.java +++ b/src/main/java/ast/statement/LocalVariableDeclarationNode.java @@ -4,7 +4,7 @@ import ast.ASTNode; import ast.expression.ExpressionNode; import ast.type.TypeNode; -public class LocalVariableDeclarationNode implements ASTNode { +public class LocalVariableDeclarationNode extends StatementNode { TypeNode type; String identifier; String assign; diff --git a/src/main/java/ast/statement/StatementNode.java b/src/main/java/ast/statement/StatementNode.java index c47d30c..e4e3862 100644 --- a/src/main/java/ast/statement/StatementNode.java +++ b/src/main/java/ast/statement/StatementNode.java @@ -1,2 +1,6 @@ -package ast.statement;public class StatementNode { +package ast.statement; + +import ast.ASTNode; + +public class StatementNode implements ASTNode { } diff --git a/src/main/java/ast/type/TypeNode.java b/src/main/java/ast/type/TypeNode.java index 6336208..adb4892 100644 --- a/src/main/java/ast/type/TypeNode.java +++ b/src/main/java/ast/type/TypeNode.java @@ -22,4 +22,10 @@ public class TypeNode { this.type = EnumTypeNode.IDENTIFIER; } } + + + public boolean equals(TypeNode o) { + return !o.type.equals(this.type); + } + } diff --git a/src/main/java/parser/astBuilder/ASTBuilder.java b/src/main/java/parser/astBuilder/ASTBuilder.java index 7760b5c..fa59e98 100644 --- a/src/main/java/parser/astBuilder/ASTBuilder.java +++ b/src/main/java/parser/astBuilder/ASTBuilder.java @@ -74,7 +74,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor { } else { MethodNode methodNode = new MethodNode((AccessModifierNode) visit(ctx.AccessModifier()), null, true, ctx.Identifier().getText(), (BlockNode) visit(ctx.block())); for(SimpleJavaParser.ParameterContext parameter : ctx.parameterList().parameter()) { - methodNode.addParameter((ParameterNode) visit(parameter)); +// methodNode.addParameter((ParameterNode) visit(parameter)); } return methodNode; } diff --git a/src/main/java/semantic/SemanticAnalyzer.java b/src/main/java/semantic/SemanticAnalyzer.java index d3526f4..8c7e833 100644 --- a/src/main/java/semantic/SemanticAnalyzer.java +++ b/src/main/java/semantic/SemanticAnalyzer.java @@ -7,13 +7,14 @@ import java.util.List; import java.util.Objects; import ast.*; +import ast.block.BlockNode; import ast.member.*; +import ast.parameter.ParameterNode; import ast.statement.*; import ast.statement.ifstatement.IfStatementNode; import ast.type.*; import semantic.context.Context; import semantic.exeptions.AlreadyDeclearedException; -import semantic.exeptions.NotDeclearedException; import semantic.exeptions.TypeMismatchException; import typechecker.TypeCheckResult; @@ -96,10 +97,47 @@ public class SemanticAnalyzer implements SemanticVisitor { public TypeCheckResult analyze(MethodNode methodNode) { var valid = true; - currentScope.pushScope(); + for (var otherMethod : currentClass.getMethods()) { + if (Objects.equals(otherMethod , methodNode)) + break; + if (otherMethod.isSame(methodNode)) { + errors.add(new AlreadyDeclearedException( + "Method " + methodNode.identifier + " is already defined in class " + + currentClass.identifier)); + valid = false; + } + } + currentScope.pushScope(); + for (var parameter : methodNode.getParameters()) { + var result = parameter.accept(this); + valid = valid && result.isValid(); + currentScope.addLocalVar(parameter.identifier, parameter.type); + } + // Check if this method is already declared + +// currentMethodReturnType = methodDecl.getType(); +// currentNullType = currentMethodReturnType; // Solange nicht in einem Assign oder Methoden-Aufruf dieser Typ + + // gesetzt ist, ist dieser der Rückgabewert der Methode + var result = methodNode.block.accept(this); + valid = valid && result.isValid(); currentScope.popScope(); - return new TypeCheckResult(valid, null); + var resultType = result.getType(); + + /* + if (resultType == null) { + resultType = new BaseType(Primitives.VOID); + } + if (!resultType.equals(methodDecl.getType())) { + errors.add(new TypeMismatchException("Method-Declaration " + methodDecl.getIdentifier() + " with type " + + methodDecl.getType() + " has at least one Mismatching return Type:" + + TypeHelper.generateLocationString(methodDecl.line, methodDecl.column, fileName))); + valid = false; + } + + */ + return new TypeCheckResult(valid, resultType); } @Override @@ -128,4 +166,14 @@ public class SemanticAnalyzer implements SemanticVisitor { return null; } + @Override + public TypeCheckResult analyze(ParameterNode toCheck) { + return new TypeCheckResult(true, null); + } + + @Override + public TypeCheckResult analyze(BlockNode toCheck) { + return new TypeCheckResult(true, null); + } + } \ No newline at end of file diff --git a/src/main/java/semantic/SemanticVisitor.java b/src/main/java/semantic/SemanticVisitor.java index 6bdc9a8..aca196b 100644 --- a/src/main/java/semantic/SemanticVisitor.java +++ b/src/main/java/semantic/SemanticVisitor.java @@ -2,7 +2,9 @@ package semantic; import ast.*; +import ast.block.BlockNode; import ast.member.*; +import ast.parameter.ParameterNode; import ast.statement.*; import ast.statement.ifstatement.*; import typechecker.TypeCheckResult; @@ -23,4 +25,9 @@ public interface SemanticVisitor { TypeCheckResult analyze(WhileStatementNode toCheck); + TypeCheckResult analyze(ParameterNode toCheck); + + TypeCheckResult analyze(BlockNode toCheck); + + } \ No newline at end of file diff --git a/src/test/java/semantic/Mocker.java b/src/test/java/semantic/Mocker.java index a60e83b..5a5e014 100644 --- a/src/test/java/semantic/Mocker.java +++ b/src/test/java/semantic/Mocker.java @@ -2,7 +2,13 @@ package semantic; import ast.*; import ast.block.BlockNode; +import ast.member.FieldNode; import ast.member.MethodNode; +import ast.parameter.ParameterNode; +import ast.statement.StatementNode; +import ast.statement.statementexpression.NewDeclarationStatementExpressionNode; +import ast.type.AccessModifierNode; +import ast.type.TypeNode; import java.util.ArrayList; import java.util.List; @@ -11,26 +17,94 @@ import static parser.generated.SimpleJavaParser.Identifier; public class Mocker { - public static ProgramNode mockCorrectClass(){ + public static ASTNode mockTwoSameFields(){ ProgramNode p = new ProgramNode(); ClassNode c = new ClassNode(); c.identifier = "testClass"; - MethodNode m = new MethodNode(); + FieldNode f1 = new FieldNode(new AccessModifierNode("public"), new TypeNode("int"), "a"); - BlockNode b = new BlockNode(); + c.members.add(f1); + FieldNode f2 = new FieldNode(new AccessModifierNode("public"), new TypeNode("int"), "a"); - -// b.statements.add(); - - m.block = b; - - c.members.add(m); + c.members.add(f2); p.classes.add(c); return p; } + public static ASTNode mockSimpleMethod(){ + ProgramNode p = new ProgramNode(); + + ClassNode c = new ClassNode(); + + MethodNode methodNode = new MethodNode(); + + //Parameter + ParameterNode parameterNode = new ParameterNode(new TypeNode("int"), "a"); + + methodNode.addParameter(parameterNode); + + //Statements + + //Block + methodNode.block = new BlockNode(); + + c.members.add(methodNode); + + p.classes.add(c); + + return p; + } + + public static ASTNode mockTwoSameMethods(){ + ProgramNode p = new ProgramNode(); + + ClassNode c = new ClassNode(); + + MethodNode methodNode = new MethodNode(); + methodNode.block = new BlockNode(); + methodNode.type = new TypeNode("int"); + + methodNode.identifier = "testMethod"; + + c.members.add(methodNode); + + MethodNode methodNode1 = new MethodNode(); + methodNode1.block = new BlockNode(); + methodNode1.type = new TypeNode("int"); + + methodNode1.identifier = "testMethod"; + + c.members.add(methodNode1); + + p.classes.add(c); + + return p; + } + + public static ASTNode mockTwoDifferentMethods(){ + ProgramNode p = new ProgramNode(); + + ClassNode c = new ClassNode(); + + MethodNode methodNode = new MethodNode(); + methodNode.block = new BlockNode(); + methodNode.identifier = "testMethod"; + + c.members.add(methodNode); + + MethodNode methodNode1 = new MethodNode(); + methodNode1.block = new BlockNode(); + methodNode1.identifier = "testMethod1"; + + c.members.add(methodNode1); + + p.classes.add(c); + + return p; + } + } diff --git a/src/test/java/semantic/SemanticTest.java b/src/test/java/semantic/SemanticTest.java index 8554022..7ebc7ad 100644 --- a/src/test/java/semantic/SemanticTest.java +++ b/src/test/java/semantic/SemanticTest.java @@ -1,14 +1,63 @@ package semantic; +import ast.ASTNode; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import semantic.exeptions.AlreadyDeclearedException; + +import static org.junit.jupiter.api.Assertions.*; public class SemanticTest { + @BeforeEach + public void setup(){ + SemanticAnalyzer.clearAnalyzier(); + } + @Test - public void correctClass(){ + public void twoFieldsSameName() { + ASTNode ast = Mocker.mockTwoSameFields(); + ASTNode tast = SemanticAnalyzer.generateTast(ast); + + assertEquals(SemanticAnalyzer.errors.size(), 1); + assertInstanceOf(AlreadyDeclearedException.class, SemanticAnalyzer.errors.getFirst()); + assertNull(tast); } + @Test + public void simpleMethod(){ + + ASTNode ast = Mocker.mockSimpleMethod(); + + ASTNode tast = SemanticAnalyzer.generateTast(ast); + + assertEquals(SemanticAnalyzer.errors.size(), 0); + assertNotNull(tast); + + } + + @Test + public void twoSameMethods(){ + ASTNode ast = Mocker.mockTwoSameMethods(); + + ASTNode tast = SemanticAnalyzer.generateTast(ast); + + assertEquals(1, SemanticAnalyzer.errors.size()); + assertInstanceOf(AlreadyDeclearedException.class, SemanticAnalyzer.errors.getFirst()); + assertNull(tast); + } + + @Test + public void twoDifferentMethods(){ + ASTNode ast = Mocker.mockTwoDifferentMethods(); + + ASTNode tast = SemanticAnalyzer.generateTast(ast); + + assertEquals(SemanticAnalyzer.errors.size(), 0); + assertNotNull(tast); + } + } diff --git a/src/test/java/semantic/endToTAST/CorrectTest.java b/src/test/java/semantic/endToTAST/CorrectTest.java index a8280fe..e3a7a08 100644 --- a/src/test/java/semantic/endToTAST/CorrectTest.java +++ b/src/test/java/semantic/endToTAST/CorrectTest.java @@ -1,5 +1,6 @@ package semantic.endToTAST; +import ast.ASTNode; import ast.ProgramNode; import org.antlr.v4.runtime.CharStream; import org.antlr.v4.runtime.CharStreams; @@ -9,10 +10,13 @@ import org.junit.jupiter.api.Test; import parser.astBuilder.ASTBuilder; import parser.generated.SimpleJavaLexer; import parser.generated.SimpleJavaParser; +import semantic.SemanticAnalyzer; import java.io.IOException; import java.nio.file.Paths; +import static org.junit.jupiter.api.Assertions.assertEquals; + public class CorrectTest { @Test @@ -36,6 +40,12 @@ public class CorrectTest { System.out.println("Test"); + ASTNode tast = SemanticAnalyzer.generateTast(abstractSyntaxTree); + + System.out.println("Errors: " + SemanticAnalyzer.errors.size()); + + assertEquals(SemanticAnalyzer.errors.size(), 0); + } } diff --git a/src/test/resources/semantic/endToTAST/Test.java b/src/test/resources/semantic/endToTAST/Test.java index bd4e673..d067998 100644 --- a/src/test/resources/semantic/endToTAST/Test.java +++ b/src/test/resources/semantic/endToTAST/Test.java @@ -1,7 +1,6 @@ public class Test { - public void test(){ - - } + public boolean b; + public boolean b; } \ No newline at end of file