From a34c7ded50cebdeb290c93f0d78053bbd58581e1 Mon Sep 17 00:00:00 2001 From: Bruder John Date: Thu, 27 Jun 2024 15:04:01 +0200 Subject: [PATCH] added Some Semantic Checks --- src/main/java/ast/members/MethodNode.java | 2 +- src/main/java/ast/statements/ElseNode.java | 4 +- src/main/java/ast/statements/IfElseNode.java | 8 +- src/main/java/ast/statements/IfNode.java | 6 +- src/main/java/ast/type/ValueNode.java | 11 +- src/main/java/semantic/SemanticAnalyzer.java | 153 +++++++++++++----- src/main/java/semantic/SemanticVisitor.java | 3 + .../semantic/exceptions/WrongOverloading.java | 9 ++ src/test/java/semantic/EndToTypedAstTest.java | 3 + src/test/java/semantic/SemanticTest.java | 12 ++ .../IfExpressionInt.java | 14 ++ .../MissingOverloadingParameter.java | 19 +++ .../MultipleReturnTypes.java | 2 +- .../CorrectMethodParameter.java | 18 +++ .../typedAstFeaturesTests/CorrectTest.java | 20 ++- .../IfExpressionBoolean.java | 13 ++ .../input/typedAstFeaturesTests/IfReturn.java | 13 ++ .../VoidReturnTypeIF.java | 13 ++ 18 files changed, 267 insertions(+), 56 deletions(-) create mode 100644 src/main/java/semantic/exceptions/WrongOverloading.java create mode 100644 src/test/resources/input/typedAstExceptionsTests/IfExpressionInt.java create mode 100644 src/test/resources/input/typedAstExceptionsTests/MissingOverloadingParameter.java create mode 100644 src/test/resources/input/typedAstFeaturesTests/CorrectMethodParameter.java create mode 100644 src/test/resources/input/typedAstFeaturesTests/IfExpressionBoolean.java create mode 100644 src/test/resources/input/typedAstFeaturesTests/IfReturn.java create mode 100644 src/test/resources/input/typedAstFeaturesTests/VoidReturnTypeIF.java diff --git a/src/main/java/ast/members/MethodNode.java b/src/main/java/ast/members/MethodNode.java index 5e10d48..1345508 100644 --- a/src/main/java/ast/members/MethodNode.java +++ b/src/main/java/ast/members/MethodNode.java @@ -46,7 +46,7 @@ public class MethodNode implements MemberNode, Visitable { } for (int i = 0; i < this.getParameters().size(); i++) { - if (this.getParameters().get(i).type.equals(methodNode.getParameters().get(i).type)) { + if (!this.getParameters().get(i).type.equals(methodNode.getParameters().get(i).type)) { return false; } } diff --git a/src/main/java/ast/statements/ElseNode.java b/src/main/java/ast/statements/ElseNode.java index 78b62f1..7099895 100644 --- a/src/main/java/ast/statements/ElseNode.java +++ b/src/main/java/ast/statements/ElseNode.java @@ -5,7 +5,7 @@ import semantic.SemanticVisitor; import typechecker.TypeCheckResult; public class ElseNode implements IStatementNode { - BlockNode block; + public BlockNode block; public ElseNode(BlockNode block) { this.block = block; @@ -14,6 +14,6 @@ public class ElseNode implements IStatementNode { @Override public TypeCheckResult accept(SemanticVisitor visitor) { - return null; + return visitor.analyze(this); } } diff --git a/src/main/java/ast/statements/IfElseNode.java b/src/main/java/ast/statements/IfElseNode.java index 34d40f3..55d4191 100644 --- a/src/main/java/ast/statements/IfElseNode.java +++ b/src/main/java/ast/statements/IfElseNode.java @@ -8,9 +8,9 @@ import java.util.ArrayList; import java.util.List; public class IfElseNode implements IStatementNode { - IfNode ifStatement; - List elseIfStatements = new ArrayList<>(); - ElseNode elseStatement; + public IfNode ifStatement; + public List elseIfStatements = new ArrayList<>(); + public ElseNode elseStatement; public IfElseNode(IfNode ifStatement, ElseNode elseNode) { this.ifStatement = ifStatement; @@ -23,6 +23,6 @@ public class IfElseNode implements IStatementNode { @Override public TypeCheckResult accept(SemanticVisitor visitor) { - return null; + return visitor.analyze(this); } } diff --git a/src/main/java/ast/statements/IfNode.java b/src/main/java/ast/statements/IfNode.java index 2f30788..46e15c0 100644 --- a/src/main/java/ast/statements/IfNode.java +++ b/src/main/java/ast/statements/IfNode.java @@ -6,8 +6,8 @@ import semantic.SemanticVisitor; import typechecker.TypeCheckResult; public class IfNode implements IStatementNode { - IExpressionNode expression; - BlockNode block; + public IExpressionNode expression; + public BlockNode block; public IfNode(IExpressionNode expression, BlockNode block) { this.expression = expression; @@ -16,6 +16,6 @@ public class IfNode implements IStatementNode { @Override public TypeCheckResult accept(SemanticVisitor visitor) { - return null; + return visitor.analyze(this); } } diff --git a/src/main/java/ast/type/ValueNode.java b/src/main/java/ast/type/ValueNode.java index b51f799..ec35af0 100644 --- a/src/main/java/ast/type/ValueNode.java +++ b/src/main/java/ast/type/ValueNode.java @@ -1,8 +1,11 @@ package ast.type; import ast.ASTNode; +import semantic.SemanticVisitor; +import typechecker.TypeCheckResult; +import visitor.Visitable; -public class ValueNode implements ASTNode { +public class ValueNode implements ASTNode, Visitable { public EnumValueNode valueType; public String value; @@ -10,4 +13,10 @@ public class ValueNode implements ASTNode { this.valueType = valueType; this.value = value; } + + + @Override + public TypeCheckResult accept(SemanticVisitor visitor) { + return visitor.analyze(this); + } } diff --git a/src/main/java/semantic/SemanticAnalyzer.java b/src/main/java/semantic/SemanticAnalyzer.java index 2e7e496..1f318f2 100644 --- a/src/main/java/semantic/SemanticAnalyzer.java +++ b/src/main/java/semantic/SemanticAnalyzer.java @@ -25,7 +25,9 @@ import ast.statementexpressions.methodcallstatementnexpressions.MethodCallNode; import ast.statementexpressions.methodcallstatementnexpressions.TargetNode; import ast.statements.*; import ast.type.EnumAccessModifierNode; +import ast.type.ValueNode; import ast.type.type.*; +import com.sun.jdi.IntegerType; import semantic.context.Context; import semantic.exceptions.*; import typechecker.TypeCheckResult; @@ -156,11 +158,6 @@ public class SemanticAnalyzer implements SemanticVisitor { if (methodNode.getType() == null) { methodNode.setType(new BaseType(TypeEnum.VOID)); } - if (!resultType.equals(methodNode.getType())) { - errors.add(new TypeMismatchException("Method-Declaration " + methodNode.getIdentifier() + " with type " - + methodNode.getType() + " has at least one Mismatching return Type:")); - valid = false; - } return new TypeCheckResult(valid, resultType); @@ -180,6 +177,16 @@ public class SemanticAnalyzer implements SemanticVisitor { @Override public TypeCheckResult analyze(IfNode toCheck) { + toCheck.block.accept(this); + + var resultExpression = toCheck.expression.accept(this); + if(resultExpression.isValid()){ + if(!resultExpression.getType().equals(new BaseType(TypeEnum.BOOL))){ + errors.add(new TypeMismatchException("Expression must be Boolean")); + return new TypeCheckResult(false, new BaseType(TypeEnum.VOID)); + } + } + return new TypeCheckResult(true, null); } @@ -187,9 +194,14 @@ public class SemanticAnalyzer implements SemanticVisitor { public TypeCheckResult analyze(ReturnNode toCheck) { if (toCheck.expression != null) { var result = toCheck.expression.accept(this); + if(result.isValid()){ + if(!result.getType().equals(currentMethodReturnType)){ + errors.add(new TypeMismatchException("Mismatched return Type from method")); + } + } return new TypeCheckResult(true, result.getType()); } else if (toCheck.voidReturn) { - return new TypeCheckResult(false, new BaseType(TypeEnum.VOID)); + return new TypeCheckResult(true, new BaseType(TypeEnum.VOID)); } return new TypeCheckResult(true, null); @@ -216,7 +228,9 @@ public class SemanticAnalyzer implements SemanticVisitor { if (blockReturnType == null) { blockReturnType = result.getType(); } else { - errors.add(new MultipleReturnTypes("There are multiple Return types")); + if(!blockReturnType.equals(result.getType())) { + errors.add(new MultipleReturnTypes("There are multiple Return types")); + } } } } @@ -238,6 +252,7 @@ public class SemanticAnalyzer implements SemanticVisitor { @Override public TypeCheckResult analyze(ElseNode toCheck) { + toCheck.block.accept(this); return null; } @@ -289,7 +304,11 @@ public class SemanticAnalyzer implements SemanticVisitor { @Override public TypeCheckResult analyze(IfElseNode toCheck) { - return new TypeCheckResult(true, null); + var resultIf = toCheck.ifStatement.accept(this); + + var resultElse = toCheck.elseStatement.accept(this); + + return new TypeCheckResult(true, new BaseType(TypeEnum.VOID)); } @Override @@ -301,11 +320,20 @@ public class SemanticAnalyzer implements SemanticVisitor { targetType = currentFields.get(toCheck.target.identifier); } if (targetType instanceof ReferenceType reference) { - return new TypeCheckResult(true, getTypeFromMethod(toCheck, reference)); + var type = getTypeFromMethod(toCheck, reference); + if(type != null){ + return new TypeCheckResult(true, type); + }else { + return new TypeCheckResult(false, null); + } + } } else { if(toCheck.target.thisTar){ - return new TypeCheckResult(true, getTypeFromMethod(toCheck, new ReferenceType(currentClass.identifier))); + var type = getTypeFromMethod(toCheck, new ReferenceType(currentClass.identifier)); + if(type != null){ + return new TypeCheckResult(true, type); + } } else { var result = toCheck.target.accept(this); if (result.getType() instanceof ReferenceType reference) { @@ -313,27 +341,7 @@ public class SemanticAnalyzer implements SemanticVisitor { } } } - return null; - } - - private ITypeNode getTypeFromMethod(MethodCallNode toCheck, ReferenceType reference) { - var classContext = context.getClass(reference.getIdentifier()); - - if (classContext == null) { - errors.add(new NotDeclaredException(toCheck.target.identifier + "is not Defined")); - } else { - var methods = classContext.getMethods(); - for (var method : methods) { - if (toCheck.identifier.equals(method.getIdentifier()) && method.getParameters().size() == toCheck.parameters.size()) { - if(method.accesModifier.accessType == EnumAccessModifierNode.PUBLIC){ - return method.getType(); - }else { - errors.add(new NotVisibleException("This Method is not Visible")); - } - } - } - } - return null; + return new TypeCheckResult(false, null); } @Override @@ -344,6 +352,9 @@ public class SemanticAnalyzer implements SemanticVisitor { TypeCheckResult result = localVarDecl.expression.accept(this); var resultType = localVarDecl.expression.getType(); + if(result.getType() != null){ + resultType = result.getType(); + } valid = result.isValid() && valid; if (!Objects.equals(resultType, localVarDecl.type)) { @@ -410,17 +421,26 @@ public class SemanticAnalyzer implements SemanticVisitor { public TypeCheckResult analyze(UnaryNode unary) { var valid = true; - if (currentScope.contains(unary.identifier)) { - return new TypeCheckResult(valid, currentScope.getLocalVar(unary.identifier)); - } else if (currentFields.get(unary.identifier) != null) { - return new TypeCheckResult(valid, currentFields.get(unary.identifier)); - } else if (unary.statement != null) { + if(unary.identifier != null){ + if (currentScope.contains(unary.identifier)) { + return new TypeCheckResult(valid, currentScope.getLocalVar(unary.identifier)); + } else if (currentFields.get(unary.identifier) != null) { + return new TypeCheckResult(valid, currentFields.get(unary.identifier)); + } else if (unary.statement != null) { + var result = unary.statement.accept(this); + unary.setType(result.getType()); + return result; + } else { + errors.add(new NotDeclaredException("Var is not Declared")); + } + } else if (unary.statement != null){ var result = unary.statement.accept(this); - unary.setType(result.getType()); - return result; - } else { - errors.add(new NotDeclaredException("Var is not Declared")); + return new TypeCheckResult(result.isValid(), result.getType()); + } else if(unary.value != null){ + var result = unary.value.accept(this); + return new TypeCheckResult(result.isValid(), result.getType()); } + return new TypeCheckResult(valid, null); } @@ -464,6 +484,59 @@ public class SemanticAnalyzer implements SemanticVisitor { return null; } + @Override + public TypeCheckResult analyze(ValueNode valueNode) { + switch (valueNode.valueType){ + case INT_VALUE -> { + return new TypeCheckResult(true, new BaseType(TypeEnum.INT)); + } + case CHAR_VALUE -> { + return new TypeCheckResult(true, new BaseType(TypeEnum.CHAR)); + } + case BOOLEAN_VALUE -> { + return new TypeCheckResult(true, new BaseType(TypeEnum.BOOL)); + } + default -> { + return new TypeCheckResult(false, null); + } + } + } + + private ITypeNode getTypeFromMethod(MethodCallNode toCheck, ReferenceType reference) { + var classContext = context.getClass(reference.getIdentifier()); + + if (classContext == null) { + errors.add(new NotDeclaredException(toCheck.target.identifier + "is not Defined")); + } else { + var methods = classContext.getMethods(); + for (var method : methods) { + if (toCheck.identifier.equals(method.getIdentifier())) { + if(method.getParameters().size() == toCheck.parameters.size() && !(method instanceof ConstructorNode)){ + boolean same = true; + for(int i = 0; i < method.getParameters().size(); i++){ + var result1 = method.getParameters().get(i).accept(this); + var result2 = toCheck.parameters.get(i).accept(this); + if (Objects.equals(result1.getType(), result2.getType())) { + same = false; + } + } + if(same){ + if(method.accesModifier.accessType == EnumAccessModifierNode.PUBLIC){ + if(method.getType() == null){ + return new BaseType(TypeEnum.VOID); + } + return method.getType(); + }else { + errors.add(new NotVisibleException("This Method is not Visible")); + } + } + } + } + } + errors.add(new WrongOverloading("No Method found with this parameters")); + } + return null; + } } \ No newline at end of file diff --git a/src/main/java/semantic/SemanticVisitor.java b/src/main/java/semantic/SemanticVisitor.java index 87ba85a..0c979ca 100644 --- a/src/main/java/semantic/SemanticVisitor.java +++ b/src/main/java/semantic/SemanticVisitor.java @@ -14,6 +14,7 @@ import ast.statementexpressions.crementexpressions.IncrementNode; import ast.statementexpressions.methodcallstatementnexpressions.MethodCallNode; import ast.statementexpressions.methodcallstatementnexpressions.TargetNode; import ast.statements.*; +import ast.type.ValueNode; import typechecker.TypeCheckResult; public interface SemanticVisitor { @@ -70,4 +71,6 @@ public interface SemanticVisitor { TypeCheckResult analyze(TargetNode toCheck); + TypeCheckResult analyze(ValueNode toCheck); + } \ No newline at end of file diff --git a/src/main/java/semantic/exceptions/WrongOverloading.java b/src/main/java/semantic/exceptions/WrongOverloading.java new file mode 100644 index 0000000..5cb42a8 --- /dev/null +++ b/src/main/java/semantic/exceptions/WrongOverloading.java @@ -0,0 +1,9 @@ +package semantic.exceptions; + +public class WrongOverloading extends RuntimeException { + + public WrongOverloading(String message) { + super(message); + } + +} diff --git a/src/test/java/semantic/EndToTypedAstTest.java b/src/test/java/semantic/EndToTypedAstTest.java index 7d13f07..637ebc1 100644 --- a/src/test/java/semantic/EndToTypedAstTest.java +++ b/src/test/java/semantic/EndToTypedAstTest.java @@ -141,6 +141,9 @@ public class EndToTypedAstTest { ASTNode typedAst = SemanticAnalyzer.generateTast(abstractSyntaxTree); System.out.println("Testing the file: " + file.getName()); + for(Exception runtimeException : SemanticAnalyzer.errors){ + runtimeException.printStackTrace(); + } assertTrue(SemanticAnalyzer.errors.isEmpty()); assertNotNull(typedAst); } diff --git a/src/test/java/semantic/SemanticTest.java b/src/test/java/semantic/SemanticTest.java index a4039ac..58d0210 100644 --- a/src/test/java/semantic/SemanticTest.java +++ b/src/test/java/semantic/SemanticTest.java @@ -3,6 +3,18 @@ package semantic; public class SemanticTest { + public void test(){ + + } + + public void test(int a, boolean b){ + + } + + public void test(boolean b, int a){ + + } + // @Test // public void alreadyDeclaredLocalFieldVar() { // ProgramNode programNode = new ProgramNode(); diff --git a/src/test/resources/input/typedAstExceptionsTests/IfExpressionInt.java b/src/test/resources/input/typedAstExceptionsTests/IfExpressionInt.java new file mode 100644 index 0000000..2fbbbfc --- /dev/null +++ b/src/test/resources/input/typedAstExceptionsTests/IfExpressionInt.java @@ -0,0 +1,14 @@ +// @expected: TypeMismatchException +public class Test{ + + public void test(int x){ + + if(x){ + + } else { + + } + + } + +} \ No newline at end of file diff --git a/src/test/resources/input/typedAstExceptionsTests/MissingOverloadingParameter.java b/src/test/resources/input/typedAstExceptionsTests/MissingOverloadingParameter.java new file mode 100644 index 0000000..17eac69 --- /dev/null +++ b/src/test/resources/input/typedAstExceptionsTests/MissingOverloadingParameter.java @@ -0,0 +1,19 @@ +// @expected: WrongOverloading +public class Test{ + + public void test(int x){ + + if(this.get()){ + + } else { + + } + + } + public boolean b; + + public boolean get(int c){ + return b; + } + +} \ No newline at end of file diff --git a/src/test/resources/input/typedAstExceptionsTests/MultipleReturnTypes.java b/src/test/resources/input/typedAstExceptionsTests/MultipleReturnTypes.java index f368e60..b59f6b9 100644 --- a/src/test/resources/input/typedAstExceptionsTests/MultipleReturnTypes.java +++ b/src/test/resources/input/typedAstExceptionsTests/MultipleReturnTypes.java @@ -1,4 +1,4 @@ -// @expected: MultipleReturnTypes +// @expected: TypeMismatchException public class Example { public static int testMethod(int x, char c){ diff --git a/src/test/resources/input/typedAstFeaturesTests/CorrectMethodParameter.java b/src/test/resources/input/typedAstFeaturesTests/CorrectMethodParameter.java new file mode 100644 index 0000000..ecfad3e --- /dev/null +++ b/src/test/resources/input/typedAstFeaturesTests/CorrectMethodParameter.java @@ -0,0 +1,18 @@ +public class Test{ + + public void test(int x){ + + if(this.get(x)){ + + } else { + + } + + } + public boolean b; + + public boolean get(int c){ + return b; + } + +} \ No newline at end of file diff --git a/src/test/resources/input/typedAstFeaturesTests/CorrectTest.java b/src/test/resources/input/typedAstFeaturesTests/CorrectTest.java index 1f51430..e5219be 100644 --- a/src/test/resources/input/typedAstFeaturesTests/CorrectTest.java +++ b/src/test/resources/input/typedAstFeaturesTests/CorrectTest.java @@ -1,9 +1,21 @@ -public class Car{ +public class Test{ - private int speed; + public int i; + public boolean b; - public int getSpeed(boolean boo){ - return speed; + public int test(){ + + return i; + + } + + public void test(int a){ + + } + + public int test(boolean bool){ + int ret = 1; + return ret; } } \ No newline at end of file diff --git a/src/test/resources/input/typedAstFeaturesTests/IfExpressionBoolean.java b/src/test/resources/input/typedAstFeaturesTests/IfExpressionBoolean.java new file mode 100644 index 0000000..526a2f2 --- /dev/null +++ b/src/test/resources/input/typedAstFeaturesTests/IfExpressionBoolean.java @@ -0,0 +1,13 @@ +public class Car{ + + public void test(boolean boo){ + + if(boo){ + + } else { + + } + + } + +} \ No newline at end of file diff --git a/src/test/resources/input/typedAstFeaturesTests/IfReturn.java b/src/test/resources/input/typedAstFeaturesTests/IfReturn.java new file mode 100644 index 0000000..00cd263 --- /dev/null +++ b/src/test/resources/input/typedAstFeaturesTests/IfReturn.java @@ -0,0 +1,13 @@ +public class Car{ + + public int getSpeed(boolean bool, int a, int b){ + + if(bool){ + return a; + } else { + return b; + } + + } + +} \ No newline at end of file diff --git a/src/test/resources/input/typedAstFeaturesTests/VoidReturnTypeIF.java b/src/test/resources/input/typedAstFeaturesTests/VoidReturnTypeIF.java new file mode 100644 index 0000000..bc31a27 --- /dev/null +++ b/src/test/resources/input/typedAstFeaturesTests/VoidReturnTypeIF.java @@ -0,0 +1,13 @@ +public class Car{ + + private int speed; + + public void getSpeed(boolean boo){ + if(boo){ + + } else { + + } + } + +} \ No newline at end of file