diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml index 9ee63e3..d81162c 100644 --- a/pom.xml +++ b/pom.xml @@ -27,13 +27,22 @@ antlr4-runtime 4.13.1 - org.ow2.asm asm 9.7 - + + com.fasterxml.jackson.core + jackson-databind + 2.16.0 + + + org.assertj + assertj-core + 3.26.0 + test + diff --git a/src/main/java/ast/ClassNode.java b/src/main/java/ast/ClassNode.java index f1f5a4c..26310f8 100644 --- a/src/main/java/ast/ClassNode.java +++ b/src/main/java/ast/ClassNode.java @@ -20,6 +20,8 @@ public class ClassNode implements ASTNode, Visitable { public List members = new ArrayList<>(); public boolean hasConstructor = false; + public ClassNode() {} + public ClassNode(AccessTypeNode accessType, String identifier){ this.accessType = accessType; this.identifier = identifier; diff --git a/src/main/java/ast/expression/ExpressionNode.java b/src/main/java/ast/expression/ExpressionNode.java index e790795..42be180 100644 --- a/src/main/java/ast/expression/ExpressionNode.java +++ b/src/main/java/ast/expression/ExpressionNode.java @@ -1,8 +1,25 @@ package ast.expression; import ast.ASTNode; +import ast.statement.AssignmentStatementNode; +import ast.statement.IfStatementNode; +import ast.statement.VariableDeclarationStatementNode; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; import visitor.Visitable; +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = BinaryExpressionNode.class, name = "Binary"), + @JsonSubTypes.Type(value = This.class, name = "This"), + @JsonSubTypes.Type(value = InstVar.class, name = "InstVar"), + @JsonSubTypes.Type(value = IdentifierExpressionNode.class, name = "Identifier"), + @JsonSubTypes.Type(value = LiteralNode.class, name = "Literal"), + @JsonSubTypes.Type(value = UnaryExpressionNode.class, name = "Unary")} +) + public interface ExpressionNode extends ASTNode, Visitable { } diff --git a/src/main/java/ast/expression/ExpresssionOperator.java b/src/main/java/ast/expression/ExpresssionOperator.java index b1f62a3..42bc137 100644 --- a/src/main/java/ast/expression/ExpresssionOperator.java +++ b/src/main/java/ast/expression/ExpresssionOperator.java @@ -1,13 +1,13 @@ package ast.expression; public enum ExpresssionOperator { - DOT, // . + DOT, // . NICHT MEHR GEBRAUCHT PLUS, // + MINUS, // - MULTIPLY, // * DIVIDE, // / NOT, // ! - ASSIGNMENT, // = + ASSIGNMENT, // = (NICHT MEHR GEBRAUCHT ??) EQUALS, // == UNEQUALS, // != ERROR //TODO: Remove This diff --git a/src/main/java/ast/expression/InstVar.java b/src/main/java/ast/expression/InstVar.java new file mode 100644 index 0000000..6ffbba3 --- /dev/null +++ b/src/main/java/ast/expression/InstVar.java @@ -0,0 +1,24 @@ +package ast.expression; + +import ast.type.TypeNode; +import semantic.SemanticVisitor; +import typechecker.TypeCheckResult; +import visitor.Visitable; + +public class InstVar implements ExpressionNode, Visitable { + public String identifier; + public ExpressionNode expression; + public TypeNode type; + + public InstVar(){} + + public InstVar(ExpressionNode expression, String identifier) { + this.identifier = identifier; + this.expression = expression; + } + + @Override + public TypeCheckResult accept(SemanticVisitor visitor) { + return visitor.analyze(this); + } +} diff --git a/src/main/java/ast/LiteralNode.java b/src/main/java/ast/expression/LiteralNode.java similarity index 61% rename from src/main/java/ast/LiteralNode.java rename to src/main/java/ast/expression/LiteralNode.java index 3873465..d97f7df 100644 --- a/src/main/java/ast/LiteralNode.java +++ b/src/main/java/ast/expression/LiteralNode.java @@ -1,29 +1,31 @@ -package ast; +package ast.expression; -import ast.expression.ExpressionNode; +import ast.type.TypeNode; import semantic.SemanticVisitor; import typechecker.TypeCheckResult; public class LiteralNode implements ExpressionNode { int value; - private String type; + private TypeNode type; + + public LiteralNode(){} public LiteralNode(int value) { this.value = value; } - public String getType() { + public TypeNode getType() { return type; } - public void setType(String type) { + public void setType(TypeNode type) { this.type = type; } @Override public TypeCheckResult accept(SemanticVisitor visitor) { - return null; + return visitor.analyze(this); } } diff --git a/src/main/java/ast/expression/This.java b/src/main/java/ast/expression/This.java new file mode 100644 index 0000000..29fc827 --- /dev/null +++ b/src/main/java/ast/expression/This.java @@ -0,0 +1,26 @@ +package ast.expression; + +import ast.ASTNode; +import ast.type.ReferenceTypeNode; +import ast.type.TypeNode; +import semantic.SemanticVisitor; +import typechecker.TypeCheckResult; + +public class This implements ASTNode, ExpressionNode { + private TypeNode type; + + public This(){} + + public This(String className) { + type = new ReferenceTypeNode(className); + } + + @Override + public TypeCheckResult accept(SemanticVisitor visitor) { + return visitor.analyze(this); + } + + public TypeNode getType() { + return type; + } +} diff --git a/src/main/java/ast/member/FieldNode.java b/src/main/java/ast/member/FieldNode.java index 60c5411..7273f6b 100644 --- a/src/main/java/ast/member/FieldNode.java +++ b/src/main/java/ast/member/FieldNode.java @@ -12,6 +12,8 @@ public class FieldNode implements MemberNode, Visitable { public TypeNode type; public String identifier; + public FieldNode(){} + public FieldNode(AccessTypeNode accessTypeNode, TypeNode type, String name){ this.accessTypeNode = accessTypeNode; this.type = type; diff --git a/src/main/java/ast/member/MemberNode.java b/src/main/java/ast/member/MemberNode.java index f186835..ace0d6d 100644 --- a/src/main/java/ast/member/MemberNode.java +++ b/src/main/java/ast/member/MemberNode.java @@ -1,6 +1,18 @@ package ast.member; import ast.ASTNode; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = MethodNode.class, name = "Method"), + + @JsonSubTypes.Type(value = FieldNode.class, name = "Field") } +) public interface MemberNode extends ASTNode { + } diff --git a/src/main/java/ast/member/MethodNode.java b/src/main/java/ast/member/MethodNode.java index bd67bc4..f3cba60 100644 --- a/src/main/java/ast/member/MethodNode.java +++ b/src/main/java/ast/member/MethodNode.java @@ -22,6 +22,8 @@ public class MethodNode implements MemberNode, Visitable { public List statements = new ArrayList<>(); + public MethodNode(){} + public MethodNode(AccessTypeNode visibility, TypeNode type, String identifier, ParameterListNode parameters, List statements){ this.visibility = visibility; diff --git a/src/main/java/ast/parameter/ParameterListNode.java b/src/main/java/ast/parameter/ParameterListNode.java index ff1c58d..76a39fb 100644 --- a/src/main/java/ast/parameter/ParameterListNode.java +++ b/src/main/java/ast/parameter/ParameterListNode.java @@ -8,6 +8,8 @@ import java.util.List; public class ParameterListNode implements ASTNode { public List parameters = new ArrayList<>(); + public ParameterListNode() {} + public ParameterListNode(List parameters){ this.parameters = parameters; } diff --git a/src/main/java/ast/parameter/ParameterNode.java b/src/main/java/ast/parameter/ParameterNode.java index f3f5193..176e987 100644 --- a/src/main/java/ast/parameter/ParameterNode.java +++ b/src/main/java/ast/parameter/ParameterNode.java @@ -7,6 +7,8 @@ public class ParameterNode implements ASTNode { public TypeNode type; public String identifier; + public ParameterNode(){} + public ParameterNode(TypeNode type, String identifier) { this.type = type; this.identifier = identifier; diff --git a/src/main/java/ast/statement/AssignmentStatementNode.java b/src/main/java/ast/statement/AssignmentStatementNode.java index f9fd38e..abd00a5 100644 --- a/src/main/java/ast/statement/AssignmentStatementNode.java +++ b/src/main/java/ast/statement/AssignmentStatementNode.java @@ -1,15 +1,22 @@ package ast.statement; import ast.expression.BinaryExpressionNode; +import ast.expression.ExpressionNode; +import ast.type.TypeNode; import semantic.SemanticVisitor; import typechecker.TypeCheckResult; import visitor.Visitable; public class AssignmentStatementNode extends StatementNode implements Visitable { - public BinaryExpressionNode expression; + public ExpressionNode expressionLeft; + public ExpressionNode expressionRight; +// public TypeNode type; - public AssignmentStatementNode(BinaryExpressionNode expression) { - this.expression = expression; + public AssignmentStatementNode(){} + + public AssignmentStatementNode(ExpressionNode expressionLeft, ExpressionNode expressionRight) { + this.expressionLeft = expressionLeft; + this.expressionRight = expressionRight; } @Override diff --git a/src/main/java/ast/statement/StatementNode.java b/src/main/java/ast/statement/StatementNode.java index eff1804..5a96ee5 100644 --- a/src/main/java/ast/statement/StatementNode.java +++ b/src/main/java/ast/statement/StatementNode.java @@ -1,8 +1,22 @@ package ast.statement; import ast.ASTNode; +import ast.type.BaseTypeNode; +import ast.type.ReferenceTypeNode; import visitor.Visitable; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = IfStatementNode.class, name = "IF"), + @JsonSubTypes.Type(value = AssignmentStatementNode.class, name = "Assignment"), + @JsonSubTypes.Type(value = VariableDeclarationStatementNode.class, name = "VariableDeclaration") } +) + public abstract class StatementNode implements ASTNode, Visitable { } diff --git a/src/main/java/ast/statement/VariableDeclarationStatementNode.java b/src/main/java/ast/statement/VariableDeclarationStatementNode.java index 4302177..9feb086 100644 --- a/src/main/java/ast/statement/VariableDeclarationStatementNode.java +++ b/src/main/java/ast/statement/VariableDeclarationStatementNode.java @@ -9,6 +9,9 @@ public class VariableDeclarationStatementNode extends StatementNode { public TypeNode type; public String identifier; public ExpressionNode expression; + + public VariableDeclarationStatementNode(){} + public VariableDeclarationStatementNode(TypeNode type, String identifier, ExpressionNode expression) { this.type = type; this.identifier = identifier; diff --git a/src/main/java/ast/type/AccessTypeNode.java b/src/main/java/ast/type/AccessTypeNode.java index ba156c1..4e0db89 100644 --- a/src/main/java/ast/type/AccessTypeNode.java +++ b/src/main/java/ast/type/AccessTypeNode.java @@ -5,6 +5,8 @@ import ast.ASTNode; public class AccessTypeNode implements ASTNode { public EnumAccessTypeNode enumAccessTypeNode; + public AccessTypeNode(){} + public AccessTypeNode(EnumAccessTypeNode enumAccessTypeNode) { this.enumAccessTypeNode = enumAccessTypeNode; } diff --git a/src/main/java/ast/type/BaseTypeNode.java b/src/main/java/ast/type/BaseTypeNode.java index 2d871ab..8a9a4a6 100644 --- a/src/main/java/ast/type/BaseTypeNode.java +++ b/src/main/java/ast/type/BaseTypeNode.java @@ -6,8 +6,23 @@ public class BaseTypeNode implements ASTNode, TypeNode { public EnumTypeNode enumType; + public BaseTypeNode(){} + public BaseTypeNode(EnumTypeNode enumType) { 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/ReferenceTypeNode.java b/src/main/java/ast/type/ReferenceTypeNode.java index 88225f3..91da6bc 100644 --- a/src/main/java/ast/type/ReferenceTypeNode.java +++ b/src/main/java/ast/type/ReferenceTypeNode.java @@ -3,4 +3,34 @@ package ast.type; import ast.ASTNode; public class ReferenceTypeNode implements ASTNode, TypeNode { + + private String identifier; + + public ReferenceTypeNode() {} + + public ReferenceTypeNode(String identifier) { + this.identifier = identifier; + } + + public String getIdentifier() { + return identifier; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + ReferenceTypeNode other = (ReferenceTypeNode) obj; + if (identifier == null) { + if (other.identifier != null) + return false; + } else if (!identifier.equals(other.identifier)) + return false; + return true; + } + } diff --git a/src/main/java/ast/type/TypeNode.java b/src/main/java/ast/type/TypeNode.java index 79be9c9..a855f09 100644 --- a/src/main/java/ast/type/TypeNode.java +++ b/src/main/java/ast/type/TypeNode.java @@ -2,5 +2,17 @@ package ast.type; import ast.ASTNode; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY) +@JsonSubTypes({ + @JsonSubTypes.Type(value = BaseTypeNode.class, name = "Base"), + + @JsonSubTypes.Type(value = ReferenceTypeNode.class, name = "Reference") } +) + public interface TypeNode extends ASTNode { } \ No newline at end of file diff --git a/src/main/java/classFileOutput/Example.class b/src/main/java/classFileOutput/Example.class index 522ae9e..e69de29 100644 Binary files a/src/main/java/classFileOutput/Example.class and b/src/main/java/classFileOutput/Example.class differ diff --git a/src/main/java/parser/ASTBuilder.java b/src/main/java/parser/ASTBuilder.java index 0f3b09c..9110f2c 100644 --- a/src/main/java/parser/ASTBuilder.java +++ b/src/main/java/parser/ASTBuilder.java @@ -1,11 +1,7 @@ package parser; import ast.*; -import ast.expression.BinaryExpressionNode; -import ast.expression.ExpressionNode; -import ast.expression.ExpresssionOperator; -import ast.expression.IdentifierExpressionNode; -import ast.expression.UnaryExpressionNode; +import ast.expression.*; import ast.member.FieldNode; import ast.member.MemberNode; import ast.member.MethodNode; @@ -138,9 +134,8 @@ public class ASTBuilder extends SimpleJavaBaseVisitor { @Override public ASTNode visitAssignmentStatement(SimpleJavaParser.AssignmentStatementContext ctx) { - - BinaryExpressionNode expression = (BinaryExpressionNode) visit(ctx.expression()); - return new AssignmentStatementNode(expression); + ExpressionNode expression = (ExpressionNode) visit(ctx.expression()); + return null; } @Override @@ -211,7 +206,7 @@ public class ASTBuilder extends SimpleJavaBaseVisitor { int intValue = Integer.parseInt(literalContext.getText()); LiteralNode literalNode = new LiteralNode(intValue); - literalNode.setType("int"); + literalNode.setType(new BaseTypeNode(EnumTypeNode.INT)); return literalNode; } catch (NumberFormatException ignored) {} diff --git a/src/main/java/semantic/SemanticAnalyzer.java b/src/main/java/semantic/SemanticAnalyzer.java index 9ccfe98..2d95119 100644 --- a/src/main/java/semantic/SemanticAnalyzer.java +++ b/src/main/java/semantic/SemanticAnalyzer.java @@ -2,10 +2,7 @@ package semantic; import ast.*; -import ast.expression.BinaryExpressionNode; -import ast.expression.IdentifierExpressionNode; -import ast.expression.UnaryExpressionNode; -import ast.member.ConstructorNode; +import ast.expression.*; import ast.member.FieldNode; import ast.member.MemberNode; @@ -13,19 +10,30 @@ import ast.member.MethodNode; import ast.parameter.ParameterListNode; import ast.parameter.ParameterNode; import ast.statement.*; +import ast.type.ReferenceTypeNode; +import ast.expression.This; 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.TypeNode; +import semantic.context.Context; import semantic.exeptions.AlreadyDeclearedException; +import semantic.exeptions.NotDeclearedException; +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<>(); + private Context context; private static Scope currentScope; private static ClassNode currentClass; @@ -56,6 +64,7 @@ public class SemanticAnalyzer implements SemanticVisitor { var valid = true; currentScope = new Scope(); + context = new Context(node); List classes = node.classes; for (ClassNode classNode : classes) { @@ -70,7 +79,7 @@ public class SemanticAnalyzer implements SemanticVisitor { var valid = true; currentClass = classNode; - + currentFields.clear(); List members = classNode.members; for (MemberNode memberNode : members) { if (memberNode instanceof FieldNode fieldNode) { @@ -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); } @@ -146,21 +155,59 @@ public class SemanticAnalyzer implements SemanticVisitor { @Override public TypeCheckResult analyze(AssignmentStatementNode assignmentStatementNode) { boolean valid = true; - BinaryExpressionNode binaryExpressionNode = assignmentStatementNode.expression; - var result = binaryExpressionNode.accept(this); - valid = valid && result.isValid(); + ExpressionNode expressionNodeLeft = assignmentStatementNode.expressionLeft; + var resultLeft = expressionNodeLeft.accept(this); + valid = valid && resultLeft.isValid(); + ExpressionNode expressionNodeRight = assignmentStatementNode.expressionRight; + var resultRight = expressionNodeRight.accept(this); + valid = valid && resultRight.isValid(); + + if(Objects.equals(resultLeft.getType(), resultRight.getType())){ + System.out.println("SAME TYPE"); + } else { + errors.add(new TypeMismatchException("Type mismatch")); + valid = false; + } + return new TypeCheckResult(valid, null); } @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, null); + } 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 +241,40 @@ public class SemanticAnalyzer implements SemanticVisitor { return null; } + @Override + public TypeCheckResult analyze(LiteralNode toCheck) { + return new TypeCheckResult(true, toCheck.getType()); + } + + @Override + public TypeCheckResult analyze(InstVar toCheck) { + boolean valid = true; + + var result = toCheck.expression.accept(this); + + if(result.getType() instanceof BaseTypeNode){ + throw new RuntimeException("BaseType has no Methods or Fields"); + } else { + //Get typ of Field + + var type = (ReferenceTypeNode)result.getType(); + var classContext = context.getClass(type.getIdentifier()); + + if(classContext == null){ + errors.add(new NotDeclearedException("Not declared " + type.getIdentifier() + " in this scope")); + return new TypeCheckResult(false, null); + } else { + var field = classContext.getField(toCheck.identifier); + + return new TypeCheckResult(valid, field.getType()); + } + } + + } + + @Override + public TypeCheckResult analyze(This toCheck) { + return new TypeCheckResult(true, toCheck.getType()); + } + } \ No newline at end of file diff --git a/src/main/java/semantic/SemanticVisitor.java b/src/main/java/semantic/SemanticVisitor.java index 66db83e..b66ea75 100644 --- a/src/main/java/semantic/SemanticVisitor.java +++ b/src/main/java/semantic/SemanticVisitor.java @@ -2,13 +2,16 @@ package semantic; import ast.ClassNode; +import ast.expression.LiteralNode; import ast.ProgramNode; import ast.expression.BinaryExpressionNode; import ast.expression.IdentifierExpressionNode; +import ast.expression.InstVar; import ast.expression.UnaryExpressionNode; import ast.member.FieldNode; import ast.member.MethodNode; import ast.statement.*; +import ast.expression.This; import typechecker.TypeCheckResult; public interface SemanticVisitor { @@ -36,4 +39,10 @@ public interface SemanticVisitor { TypeCheckResult analyze(ReturnStatementNode toCheck); TypeCheckResult analyze(WhileStatementNode toCheck); + + TypeCheckResult analyze(LiteralNode toCheck); + + TypeCheckResult analyze(InstVar toCheck); + + TypeCheckResult analyze(This toCheck); } \ No newline at end of file diff --git a/src/main/java/semantic/context/ClassContext.java b/src/main/java/semantic/context/ClassContext.java new file mode 100644 index 0000000..982866f --- /dev/null +++ b/src/main/java/semantic/context/ClassContext.java @@ -0,0 +1,27 @@ +package semantic.context; + +import ast.ClassNode; +import ast.member.FieldNode; +import java.util.HashMap; + +public class ClassContext { + + private HashMap fields; + + public ClassContext(ClassNode classNode) { + + fields = new HashMap<>(); + + classNode.members.forEach(member -> { + if(member instanceof FieldNode fieldNode) { + fields.put(fieldNode.identifier, new FieldContext(fieldNode)); + } + }); + + } + + public FieldContext getField(String name) { + return fields.get(name); + } + +} diff --git a/src/main/java/semantic/context/Context.java b/src/main/java/semantic/context/Context.java new file mode 100644 index 0000000..7006e65 --- /dev/null +++ b/src/main/java/semantic/context/Context.java @@ -0,0 +1,25 @@ +package semantic.context; + +import ast.ClassNode; +import ast.ProgramNode; +import java.util.HashMap; + +public class Context { + + private HashMap classes; + + public Context(ProgramNode programNode) { + classes = new HashMap<>(); + + programNode.classes.forEach(classNode -> { + ClassContext classContext = new ClassContext(classNode); + classes.put(classNode.identifier, classContext); + }); + + } + + public ClassContext getClass(String identifier) { + return classes.get(identifier); + } + +} diff --git a/src/main/java/semantic/context/FieldContext.java b/src/main/java/semantic/context/FieldContext.java new file mode 100644 index 0000000..447aa4e --- /dev/null +++ b/src/main/java/semantic/context/FieldContext.java @@ -0,0 +1,22 @@ +package semantic.context; + +import ast.member.FieldNode; +import ast.type.AccessTypeNode; +import ast.type.EnumAccessTypeNode; +import ast.type.TypeNode; + +public class FieldContext { + + private AccessTypeNode accessModifier; + private TypeNode type; + + public FieldContext(FieldNode field) { + accessModifier = field.accessTypeNode; + type = field.type; + } + + public TypeNode getType() { + return type; + } + +} diff --git a/src/main/java/semantic/exeptions/NotDeclearedException.java b/src/main/java/semantic/exeptions/NotDeclearedException.java new file mode 100644 index 0000000..aa223a0 --- /dev/null +++ b/src/main/java/semantic/exeptions/NotDeclearedException.java @@ -0,0 +1,9 @@ +package semantic.exeptions; + +public class NotDeclearedException extends RuntimeException { + + public NotDeclearedException(String message) { + super(message); + } + +} 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..4c303d8 --- /dev/null +++ b/src/test/java/semantic/Mocker.java @@ -0,0 +1,77 @@ +package semantic; + +import ast.ClassNode; +import ast.expression.LiteralNode; +import ast.ProgramNode; +import ast.expression.*; +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.*; + +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 expressionNodeLeft = new InstVar(new This("testClass"), "objectVar"); + + LiteralNode expressionNodeRight = new LiteralNode(); + expressionNodeRight.setType(new BaseTypeNode(EnumTypeNode.INT)); + + StatementNode statementNode1 = new AssignmentStatementNode(expressionNodeLeft, expressionNodeRight); + 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; + + 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..71604ee 100644 --- a/src/test/java/semantic/SemanticTest.java +++ b/src/test/java/semantic/SemanticTest.java @@ -2,29 +2,19 @@ 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; -import ast.type.EnumTypeNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.assertj.core.api.MatcherAssert; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import semantic.exeptions.AlreadyDeclearedException; +import semantic.exeptions.TypeMismatchException; -import java.util.ArrayList; -import java.util.List; +import java.io.File; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; public class SemanticTest { @@ -34,24 +24,18 @@ public class SemanticTest { } @Test - public void alreadyDeclaredLocalFieldVar(){ + 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); @@ -59,49 +43,121 @@ public class SemanticTest { } @Test - public void shouldWorkWithNoError(){ + public void alreadyDecleared() { - 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.mockFieldNodeAlreadyDeclaredProgrammNode(); - 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(1, SemanticAnalyzer.errors.size()); + assertInstanceOf(AlreadyDeclearedException.class, SemanticAnalyzer.errors.getFirst()); + assertNull(typedAst); + + } + + @Test + public void shouldWorkWithNoError() { + + //Arrange + + ProgramNode programNode = Mocker.mockCorrectProgrammNode(); + + //Act + + ASTNode typedAst = SemanticAnalyzer.generateTast(programNode); + + //Assert + assertEquals(0, SemanticAnalyzer.errors.size()); assertEquals(programNode, typedAst); } + @Test + public void refTypeCorrect() { + + //Arrange + + ObjectMapper objectMapper = new ObjectMapper(); + ProgramNode programNode = null; + try{ + programNode = objectMapper.readValue(new File("src/test/resources/semantic/correctRefType.json"), ProgramNode.class); + } catch (Exception e) { + e.printStackTrace(); + } + + //Act + + ASTNode typedAst = SemanticAnalyzer.generateTast(programNode); + + //Assert + + assertEquals(0, SemanticAnalyzer.errors.size()); + assertEquals(programNode, typedAst); + + } + + @Test + public void jsonWriteTest() { + + ObjectMapper objectMapper = new ObjectMapper(); + + //Arrange + + ProgramNode programNode = Mocker.mockCorrectProgrammNode(); + try{ + objectMapper.writeValue(new File("src/test/resources/semantic/test.json"), programNode); + } catch (Exception e) { + e.printStackTrace(); + } + + } + + @Test + public void jsonReadTest() { + + ObjectMapper objectMapper = new ObjectMapper(); + + ProgramNode programNode1 = null; + try{ + programNode1 = objectMapper.readValue(new File("src/test/resources/semantic/test.json"), ProgramNode.class); + } catch (Exception e) { + e.printStackTrace(); + } + + ProgramNode programNode2 = Mocker.mockCorrectProgrammNode(); + + } + + @Test + public void typeMismatch() { + + //Arrange + + ObjectMapper objectMapper = new ObjectMapper(); + ProgramNode programNode = null; + try{ + programNode = objectMapper.readValue(new File("src/test/resources/semantic/refTypeMismatch.json"), ProgramNode.class); + } catch (Exception e) { + e.printStackTrace(); + } + + //Act + + ASTNode typedAst = SemanticAnalyzer.generateTast(programNode); + + //Assert + + assertEquals(1, SemanticAnalyzer.errors.size()); + assertInstanceOf(TypeMismatchException.class, SemanticAnalyzer.errors.getFirst()); + assertNull(typedAst); + + } + } diff --git a/src/test/resources/semantic/correctRefType.json b/src/test/resources/semantic/correctRefType.json new file mode 100644 index 0000000..7e67bbc --- /dev/null +++ b/src/test/resources/semantic/correctRefType.json @@ -0,0 +1,70 @@ +{ + "classes": [ + { + "identifier": "testClass1", + "accessType": { + "enumAccessTypeNode": "PUBLIC" + }, + "members": [ + { + "@type": "Field", + "accessTypeNode": { + "enumAccessTypeNode": "PUBLIC" + }, + "type": { + "@type": "Base", + "enumType": "INT" + }, + "identifier": "testVar1" + }, + { + "@type": "Method", + "visibility": { + "enumAccessTypeNode": "PUBLIC" + }, + "type": { + "@type": "Base", + "enumType": "INT" + }, + "identifier": "testMethod", + "parameters": { + "parameters": [ + { + "type": { + "@type": "Base", + "enumType": "INT" + }, + "identifier": "param1" + } + ] + }, + "statements": [ + { + "@type": "Assignment", + "expressionLeft": { + "@type": "InstVar", + "identifier": "testVar1", + "expression": { + "@type": "This", + "type": { + "@type": "Reference", + "identifier": "testClass1" + } + }, + "type": null + }, + "expressionRight": { + "@type": "Literal", + "type": { + "@type": "Base", + "enumType": "INT" + } + } + } + ] + } + ], + "hasConstructor": false + } + ] +} \ No newline at end of file diff --git a/src/test/resources/semantic/refTypeMismatch.json b/src/test/resources/semantic/refTypeMismatch.json new file mode 100644 index 0000000..e5ebcd3 --- /dev/null +++ b/src/test/resources/semantic/refTypeMismatch.json @@ -0,0 +1,133 @@ +{ + "classes": [ + { + "identifier": "testClass1", + "accessType": { + "enumAccessTypeNode": "PUBLIC" + }, + "members": [ + { + "@type": "Field", + "accessTypeNode": { + "enumAccessTypeNode": "PUBLIC" + }, + "type": { + "@type": "Base", + "enumType": "INT" + }, + "identifier": "testVar1" + }, + { + "@type": "Method", + "visibility": { + "enumAccessTypeNode": "PUBLIC" + }, + "type": { + "@type": "Base", + "enumType": "INT" + }, + "identifier": "testMethod", + "parameters": { + "parameters": [ + { + "type": { + "@type": "Base", + "enumType": "INT" + }, + "identifier": "param1" + } + ] + }, + "statements": [ + { + "@type": "Assignment", + "expressionLeft": { + "@type": "InstVar", + "identifier": "testVar1", + "expression": { + "@type": "This", + "type": { + "@type": "Reference", + "identifier": "testClass1" + } + }, + "type": null + }, + "expressionRight": { + "@type": "Literal", + "type": { + "@type": "Base", + "enumType": "BOOLEAN" + } + } + } + ] + } + ], + "hasConstructor": false, + "methods": [ + { + "@type": "Method", + "visibility": { + "enumAccessTypeNode": "PUBLIC" + }, + "type": { + "@type": "Base", + "enumType": "INT" + }, + "identifier": "testMethod", + "parameters": { + "parameters": [ + { + "type": { + "@type": "Base", + "enumType": "INT" + }, + "identifier": "param1" + } + ] + }, + "statements": [ + { + "@type": "Assignment", + "expressionLeft": { + "@type": "InstVar", + "identifier": "testVar", + "expression": { + "@type": "InstVar", + "identifier": "testVar", + "expression": { + "@type": "This", + "type": { + "@type": "Reference", + "identifier": "testClass2" + } + }, + "type": null + }, + "type": null + }, + "expressionRight": { + "@type": "Literal", + "type": null + }, + "type": null + }, + { + "@type": "VariableDeclaration", + "type": { + "@type": "Base", + "enumType": "CHAR" + }, + "identifier": "objectVar", + "expression": { + "@type": "Literal", + "type": null + } + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/test/resources/semantic/test.json b/src/test/resources/semantic/test.json new file mode 100644 index 0000000..7acc30f --- /dev/null +++ b/src/test/resources/semantic/test.json @@ -0,0 +1 @@ +{"classes":[{"identifier":"testClass","accessType":{"enumAccessTypeNode":"PUBLIC"},"members":[{"@type":"Field","accessTypeNode":{"enumAccessTypeNode":"PUBLIC"},"type":{"@type":"Base","enumType":"INT"},"identifier":"testVar1"},{"@type":"Field","accessTypeNode":{"enumAccessTypeNode":"PUBLIC"},"type":{"@type":"Base","enumType":"INT"},"identifier":"objectVar"},{"@type":"Method","visibility":{"enumAccessTypeNode":"PUBLIC"},"type":{"@type":"Base","enumType":"INT"},"identifier":"testVar2","parameters":{"parameters":[{"type":{"@type":"Base","enumType":"INT"},"identifier":"param1"}]},"statements":[{"@type":"Assignment","expressionLeft":{"@type":"InstVar","identifier":"objectVar","expression":{"@type":"This","type":{"@type":"Reference","identifier":"testClass"}},"type":null},"expressionRight":{"@type":"Literal","type":{"@type":"Base","enumType":"INT"}}}]}],"hasConstructor":false,"methods":[{"@type":"Method","visibility":{"enumAccessTypeNode":"PUBLIC"},"type":{"@type":"Base","enumType":"INT"},"identifier":"testVar2","parameters":{"parameters":[{"type":{"@type":"Base","enumType":"INT"},"identifier":"param1"}]},"statements":[{"@type":"Assignment","expressionLeft":{"@type":"InstVar","identifier":"objectVar","expression":{"@type":"This","type":{"@type":"Reference","identifier":"testClass"}},"type":null},"expressionRight":{"@type":"Literal","type":{"@type":"Base","enumType":"INT"}}}]}]}]} \ No newline at end of file