From 22a30d5956fa116521bba59f130aa075e9d2c312 Mon Sep 17 00:00:00 2001 From: Bruder John Date: Fri, 31 May 2024 11:08:41 +0200 Subject: [PATCH] Added typecheck for Assignment --- src/main/java/ast/LiteralNode.java | 2 +- src/main/java/ast/type/BaseTypeNode.java | 13 +++ src/main/java/ast/type/This.java | 6 ++ src/main/java/semantic/SemanticAnalyzer.java | 49 +++++++++- src/main/java/semantic/SemanticVisitor.java | 3 + .../exeptions/TypeMismatchException.java | 9 ++ src/test/java/semantic/Mocker.java | 91 +++++++++++++++++++ src/test/java/semantic/SemanticTest.java | 82 ++++++----------- 8 files changed, 197 insertions(+), 58 deletions(-) create mode 100644 src/main/java/ast/type/This.java create mode 100644 src/main/java/semantic/exeptions/TypeMismatchException.java create mode 100644 src/test/java/semantic/Mocker.java diff --git a/src/main/java/ast/LiteralNode.java b/src/main/java/ast/LiteralNode.java index 3873465..4e7c466 100644 --- a/src/main/java/ast/LiteralNode.java +++ b/src/main/java/ast/LiteralNode.java @@ -24,6 +24,6 @@ public class LiteralNode implements ExpressionNode { @Override public TypeCheckResult accept(SemanticVisitor visitor) { - return null; + return visitor.analyze(this); } } diff --git a/src/main/java/ast/type/BaseTypeNode.java b/src/main/java/ast/type/BaseTypeNode.java index 2d871ab..eb9537c 100644 --- a/src/main/java/ast/type/BaseTypeNode.java +++ b/src/main/java/ast/type/BaseTypeNode.java @@ -10,4 +10,17 @@ public class BaseTypeNode implements ASTNode, TypeNode { this.enumType = enumType; } + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + BaseTypeNode other = (BaseTypeNode) obj; + if (enumType != other.enumType) + return false; + return true; + } } diff --git a/src/main/java/ast/type/This.java b/src/main/java/ast/type/This.java new file mode 100644 index 0000000..459eea5 --- /dev/null +++ b/src/main/java/ast/type/This.java @@ -0,0 +1,6 @@ +package ast.type; + +import ast.ASTNode; + +public class This implements ASTNode, TypeNode{ +} diff --git a/src/main/java/semantic/SemanticAnalyzer.java b/src/main/java/semantic/SemanticAnalyzer.java index 9ccfe98..a7b1704 100644 --- a/src/main/java/semantic/SemanticAnalyzer.java +++ b/src/main/java/semantic/SemanticAnalyzer.java @@ -3,6 +3,7 @@ package semantic; import ast.*; import ast.expression.BinaryExpressionNode; +import ast.expression.ExpressionNode; import ast.expression.IdentifierExpressionNode; import ast.expression.UnaryExpressionNode; import ast.member.ConstructorNode; @@ -14,15 +15,23 @@ import ast.parameter.ParameterListNode; import ast.parameter.ParameterNode; import ast.statement.*; +import java.beans.Expression; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Objects; +import ast.type.BaseTypeNode; +import ast.type.EnumTypeNode; +import ast.type.This; +import ast.type.TypeNode; import semantic.exeptions.AlreadyDeclearedException; +import semantic.exeptions.TypeMismatchException; import typechecker.TypeCheckResult; public class SemanticAnalyzer implements SemanticVisitor { - private static ArrayList currentFields = new ArrayList<>(); + private static HashMap currentFields = new HashMap<>(); public static ArrayList errors = new ArrayList<>(); @@ -134,11 +143,11 @@ public class SemanticAnalyzer implements SemanticVisitor { @Override public TypeCheckResult analyze(FieldNode toCheck) { - if (currentFields.contains(toCheck.identifier)) { + if (currentFields.get(toCheck.identifier) != null) { errors.add(new AlreadyDeclearedException("Already declared " + toCheck.identifier)); return new TypeCheckResult(false, null); } else { - currentFields.add(toCheck.identifier); + currentFields.put(toCheck.identifier, toCheck.type); } return new TypeCheckResult(true, null); } @@ -155,12 +164,39 @@ public class SemanticAnalyzer implements SemanticVisitor { @Override public TypeCheckResult analyze(BinaryExpressionNode toCheck) { boolean valid = true; + ExpressionNode left = toCheck.left; + var resultLeft = left.accept(this); + ExpressionNode right = toCheck.right; + var resultRight = right.accept(this); + + switch (toCheck.operator) { + case ASSIGNMENT: + if(Objects.equals(resultRight.getType(), resultLeft.getType())){ + System.out.println("Correct Type"); + } else { + valid = false; + errors.add(new TypeMismatchException("Type Mismatch " + resultLeft.getType() + " and " + resultRight.getType())); + } + break; + case DOT: + return new TypeCheckResult(true, resultRight.getType()); + default: + throw new RuntimeException("Unexpected operator: " + toCheck.operator); + } + return new TypeCheckResult(valid, null); } @Override public TypeCheckResult analyze(IdentifierExpressionNode toCheck) { - return null; + if(toCheck.name == "this"){ + return new TypeCheckResult(true, new This()); + } else if (currentFields.get(toCheck.name) == null) { + errors.add(new AlreadyDeclearedException("Not declared " + toCheck.name + " in this scope")); + return new TypeCheckResult(false, null); + } else { + return new TypeCheckResult(false, currentFields.get(toCheck.name)); + } } @Override @@ -194,4 +230,9 @@ public class SemanticAnalyzer implements SemanticVisitor { return null; } + @Override + public TypeCheckResult analyze(LiteralNode toCheck) { + return new TypeCheckResult(true, new BaseTypeNode(EnumTypeNode.INT)); + } + } \ No newline at end of file diff --git a/src/main/java/semantic/SemanticVisitor.java b/src/main/java/semantic/SemanticVisitor.java index 66db83e..9a8c6b1 100644 --- a/src/main/java/semantic/SemanticVisitor.java +++ b/src/main/java/semantic/SemanticVisitor.java @@ -2,6 +2,7 @@ package semantic; import ast.ClassNode; +import ast.LiteralNode; import ast.ProgramNode; import ast.expression.BinaryExpressionNode; import ast.expression.IdentifierExpressionNode; @@ -36,4 +37,6 @@ public interface SemanticVisitor { TypeCheckResult analyze(ReturnStatementNode toCheck); TypeCheckResult analyze(WhileStatementNode toCheck); + + TypeCheckResult analyze(LiteralNode toCheck); } \ No newline at end of file diff --git a/src/main/java/semantic/exeptions/TypeMismatchException.java b/src/main/java/semantic/exeptions/TypeMismatchException.java new file mode 100644 index 0000000..93fb62b --- /dev/null +++ b/src/main/java/semantic/exeptions/TypeMismatchException.java @@ -0,0 +1,9 @@ +package semantic.exeptions; + +public class TypeMismatchException extends RuntimeException { + + public TypeMismatchException(String message) { + super(message); + } + +} diff --git a/src/test/java/semantic/Mocker.java b/src/test/java/semantic/Mocker.java new file mode 100644 index 0000000..782ac7e --- /dev/null +++ b/src/test/java/semantic/Mocker.java @@ -0,0 +1,91 @@ +package semantic; + +import ast.ClassNode; +import ast.LiteralNode; +import ast.ProgramNode; +import ast.expression.BinaryExpressionNode; +import ast.expression.ExpressionNode; +import ast.expression.ExpresssionOperator; +import ast.expression.IdentifierExpressionNode; +import ast.member.FieldNode; +import ast.member.MemberNode; +import ast.member.MethodNode; +import ast.parameter.ParameterListNode; +import ast.parameter.ParameterNode; +import ast.statement.AssignmentStatementNode; +import ast.statement.StatementNode; +import ast.statement.VariableDeclarationStatementNode; +import ast.type.AccessTypeNode; +import ast.type.BaseTypeNode; +import ast.type.EnumAccessTypeNode; +import ast.type.EnumTypeNode; + +import java.util.ArrayList; +import java.util.List; + +public class Mocker { + + public static ProgramNode mockCorrectProgrammNode(){ + + ProgramNode programNode = new ProgramNode(); + List classList = new ArrayList(); + AccessTypeNode accessTypeNode = new AccessTypeNode(EnumAccessTypeNode.PUBLIC); + ClassNode classNode = new ClassNode(accessTypeNode, "testClass"); + + MemberNode memberNode1 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar1"); + classNode.members.add(memberNode1); + + MemberNode memberNode2 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "objectVar"); + classNode.members.add(memberNode2); + + List parameterNodeList = new ArrayList(); + ParameterNode parameterNode1 = new ParameterNode(new BaseTypeNode(EnumTypeNode.INT), "param1"); + parameterNodeList.add(parameterNode1); + ParameterListNode parameterListNode = new ParameterListNode(parameterNodeList); + + List statementNodeList = new ArrayList(); + + ExpressionNode expressionNodeObjectVariableLeft = new IdentifierExpressionNode("this"); + ExpressionNode expressionNodeObjectVariableRight = new IdentifierExpressionNode("objectVar"); + + ExpressionNode expressionNodeLeft = new BinaryExpressionNode(expressionNodeObjectVariableLeft, expressionNodeObjectVariableRight, ExpresssionOperator.DOT); + + ExpressionNode expressionNodeRight = new LiteralNode(1); + + BinaryExpressionNode expressionNode = new BinaryExpressionNode(expressionNodeLeft, expressionNodeRight, ExpresssionOperator.ASSIGNMENT); + + StatementNode statementNode1 = new AssignmentStatementNode(expressionNode); + statementNodeList.add(statementNode1); + + StatementNode statementNode2 = new VariableDeclarationStatementNode(new BaseTypeNode(EnumTypeNode.CHAR), "objectVar", new LiteralNode(1)); + statementNodeList.add(statementNode2); + + MemberNode memberNode3 = new MethodNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2",parameterListNode, statementNodeList ); + classNode.members.add(memberNode3); + + classList.add(classNode); + programNode.classes = classList; + + return programNode; + + } + + public static ProgramNode mockFieldNodeAlreadyDeclaredProgrammNode(){ + ProgramNode programNode = new ProgramNode(); + List classList = new ArrayList(); + AccessTypeNode accessTypeNode = new AccessTypeNode(EnumAccessTypeNode.PUBLIC); + ClassNode classNode = new ClassNode(accessTypeNode, "testClass"); + + MemberNode memberNode1 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar"); + classNode.members.add(memberNode1); + + MemberNode memberNode2 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar"); + classNode.members.add(memberNode2); + + classList.add(classNode); + programNode.classes = classList; + + return programNode; + } + +} diff --git a/src/test/java/semantic/SemanticTest.java b/src/test/java/semantic/SemanticTest.java index 83287ca..c5990de 100644 --- a/src/test/java/semantic/SemanticTest.java +++ b/src/test/java/semantic/SemanticTest.java @@ -2,17 +2,8 @@ package semantic; import ast.*; -import ast.expression.BinaryExpressionNode; -import ast.expression.ExpressionNode; -import ast.expression.ExpresssionOperator; -import ast.expression.IdentifierExpressionNode; import ast.member.FieldNode; import ast.member.MemberNode; -import ast.member.MethodNode; -import ast.parameter.ParameterListNode; -import ast.parameter.ParameterNode; -import ast.statement.AssignmentStatementNode; -import ast.statement.StatementNode; import ast.type.AccessTypeNode; import ast.type.BaseTypeNode; import ast.type.EnumAccessTypeNode; @@ -36,22 +27,35 @@ public class SemanticTest { @Test public void alreadyDeclaredLocalFieldVar(){ - ProgramNode programNode = new ProgramNode(); - List classList = new ArrayList(); - AccessTypeNode accessTypeNode = new AccessTypeNode(EnumAccessTypeNode.PUBLIC); - ClassNode classNode = new ClassNode(accessTypeNode, "testClass"); + //Arrange - MemberNode memberNode1 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar"); - classNode.members.add(memberNode1); + ProgramNode programNode = Mocker.mockFieldNodeAlreadyDeclaredProgrammNode(); - MemberNode memberNode2 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar"); - classNode.members.add(memberNode2); - - classList.add(classNode); - programNode.classes = classList; + //Act ASTNode typedAst = SemanticAnalyzer.generateTast(programNode); + //Assert + + assertEquals(1, SemanticAnalyzer.errors.size()); + assertEquals(true, SemanticAnalyzer.errors.get(0) instanceof AlreadyDeclearedException); + assertEquals(null, typedAst); + + } + + @Test + public void typeMismatch(){ + + //Arrange + + ProgramNode programNode = Mocker.mockFieldNodeAlreadyDeclaredProgrammNode(); + + //Act + + ASTNode typedAst = SemanticAnalyzer.generateTast(programNode); + + //Assert + assertEquals(1, SemanticAnalyzer.errors.size()); assertEquals(true, SemanticAnalyzer.errors.get(0) instanceof AlreadyDeclearedException); assertEquals(null, typedAst); @@ -61,44 +65,16 @@ public class SemanticTest { @Test public void shouldWorkWithNoError(){ - ProgramNode programNode = new ProgramNode(); - List classList = new ArrayList(); - AccessTypeNode accessTypeNode = new AccessTypeNode(EnumAccessTypeNode.PUBLIC); - ClassNode classNode = new ClassNode(accessTypeNode, "testClass"); + //Arrange - MemberNode memberNode1 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar1"); - classNode.members.add(memberNode1); + ProgramNode programNode = Mocker.mockCorrectProgrammNode(); - MemberNode memberNode2 = new FieldNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2"); - classNode.members.add(memberNode2); - - List parameterNodeList = new ArrayList(); - ParameterNode parameterNode1 = new ParameterNode(new BaseTypeNode(EnumTypeNode.INT), "param1"); - parameterNodeList.add(parameterNode1); - ParameterListNode parameterListNode = new ParameterListNode(parameterNodeList); - - List statementNodeList = new ArrayList(); - - ExpressionNode expressionNodeObjectVariableLeft = new IdentifierExpressionNode("this"); - ExpressionNode expressionNodeObjectVariableRight = new IdentifierExpressionNode("objectVar"); - - ExpressionNode expressionNodeLeft = new BinaryExpressionNode(expressionNodeObjectVariableLeft, expressionNodeObjectVariableRight, ExpresssionOperator.DOT); - - ExpressionNode expressionNodeRight = new LiteralNode(1); - - BinaryExpressionNode expressionNode = new BinaryExpressionNode(expressionNodeLeft, expressionNodeRight, ExpresssionOperator.ASSIGNMENT); - - StatementNode statementNode1 = new AssignmentStatementNode(expressionNode); - statementNodeList.add(statementNode1); - - MemberNode memberNode3 = new MethodNode(accessTypeNode, new BaseTypeNode(EnumTypeNode.INT), "testVar2",parameterListNode, statementNodeList ); - classNode.members.add(memberNode3); - - classList.add(classNode); - programNode.classes = classList; + //Act ASTNode typedAst = SemanticAnalyzer.generateTast(programNode); + //Assert + assertEquals(0, SemanticAnalyzer.errors.size()); assertEquals(programNode, typedAst);