Added typecheck for Assignment
Some checks failed
Gitea Actions Demo / Explore-Gitea-Actions (push) Has been cancelled

This commit is contained in:
Bruder John 2024-05-31 11:08:41 +02:00
parent c483babaf5
commit 22a30d5956
8 changed files with 197 additions and 58 deletions

View File

@ -24,6 +24,6 @@ public class LiteralNode implements ExpressionNode {
@Override
public TypeCheckResult accept(SemanticVisitor visitor) {
return null;
return visitor.analyze(this);
}
}

View File

@ -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;
}
}

View File

@ -0,0 +1,6 @@
package ast.type;
import ast.ASTNode;
public class This implements ASTNode, TypeNode{
}

View File

@ -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<String> currentFields = new ArrayList<>();
private static HashMap<String, TypeNode> currentFields = new HashMap<>();
public static ArrayList<Exception> 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));
}
}

View File

@ -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);
}

View File

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

View File

@ -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<ClassNode> classList = new ArrayList<ClassNode>();
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<ParameterNode> parameterNodeList = new ArrayList<ParameterNode>();
ParameterNode parameterNode1 = new ParameterNode(new BaseTypeNode(EnumTypeNode.INT), "param1");
parameterNodeList.add(parameterNode1);
ParameterListNode parameterListNode = new ParameterListNode(parameterNodeList);
List<StatementNode> statementNodeList = new ArrayList<StatementNode>();
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<ClassNode> classList = new ArrayList<ClassNode>();
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;
}
}

View File

@ -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<ClassNode> classList = new ArrayList<ClassNode>();
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<ClassNode> classList = new ArrayList<ClassNode>();
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<ParameterNode> parameterNodeList = new ArrayList<ParameterNode>();
ParameterNode parameterNode1 = new ParameterNode(new BaseTypeNode(EnumTypeNode.INT), "param1");
parameterNodeList.add(parameterNode1);
ParameterListNode parameterListNode = new ParameterListNode(parameterNodeList);
List<StatementNode> statementNodeList = new ArrayList<StatementNode>();
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);