diff --git a/src/main/java/ast/members/ConstructorNode.java b/src/main/java/ast/members/ConstructorNode.java index 2ff9f94..4e19933 100644 --- a/src/main/java/ast/members/ConstructorNode.java +++ b/src/main/java/ast/members/ConstructorNode.java @@ -10,14 +10,10 @@ import java.util.ArrayList; import java.util.List; import java.util.Objects; -public class ConstructorNode extends MethodNode implements Visitable { - public AccessModifierNode accessType; - public String identifier; - public List parameters = new ArrayList<>(); - public BlockNode block; +public class ConstructorNode extends MethodNode { public ConstructorNode(String accessType, String identifier, BlockNode block) { - this.accessType = new AccessModifierNode(accessType); + this.accesModifier = new AccessModifierNode(accessType); this.identifier = identifier; this.block = block; } diff --git a/src/main/java/ast/members/MethodNode.java b/src/main/java/ast/members/MethodNode.java index 46a654d..3661fb2 100644 --- a/src/main/java/ast/members/MethodNode.java +++ b/src/main/java/ast/members/MethodNode.java @@ -17,7 +17,7 @@ public class MethodNode implements MemberNode, Visitable { public AccessModifierNode accesModifier; private ITypeNode type; public Boolean voidType; - private String identifier; + protected String identifier; public List parameters = new ArrayList<>(); public BlockNode block; diff --git a/src/main/java/bytecode/MethodCodeGen.java b/src/main/java/bytecode/MethodCodeGen.java index ab6e8e1..02cf267 100644 --- a/src/main/java/bytecode/MethodCodeGen.java +++ b/src/main/java/bytecode/MethodCodeGen.java @@ -53,7 +53,7 @@ public class MethodCodeGen implements bytecode.visitor.MethodVisitor { @Override public void visit(ConstructorNode constructorNode) { methodVisitor = - classWriter.visitMethod(mapper.mapAccessTypeToOpcode(constructorNode.accessType), + classWriter.visitMethod(mapper.mapAccessTypeToOpcode(constructorNode.accesModifier), "", mapper.generateMethodDescriptor(new BaseType(TypeEnum.VOID), constructorNode.parameters), null, diff --git a/src/main/java/semantic/Scope.java b/src/main/java/semantic/Scope.java index b6b40c4..cde47c0 100644 --- a/src/main/java/semantic/Scope.java +++ b/src/main/java/semantic/Scope.java @@ -16,7 +16,7 @@ public class Scope { public void addLocalVar(String name, ITypeNode type) { if (this.contains(name)) { - throw new AlreadyDeclaredException("Variable " + name + " already exists in this scope"); + SemanticAnalyzer.errors.add(new AlreadyDeclaredException("Duplicate local variable " + name)); } localVars.peek().put(name, type); } diff --git a/src/main/java/semantic/SemanticAnalyzer.java b/src/main/java/semantic/SemanticAnalyzer.java index 6ebd124..110effd 100644 --- a/src/main/java/semantic/SemanticAnalyzer.java +++ b/src/main/java/semantic/SemanticAnalyzer.java @@ -112,53 +112,49 @@ public class SemanticAnalyzer implements SemanticVisitor { @Override public TypeCheckResult analyze(MethodNode methodNode) { - if (methodNode instanceof ConstructorNode) { - return new TypeCheckResult(true, new BaseType(TypeEnum.VOID)); - } else { - var valid = true; + var valid = true; - for (var otherMethod : currentClass.getMethods()) { - if (Objects.equals(otherMethod, methodNode)) - break; - if (otherMethod.isSame(methodNode)) { - errors.add(new AlreadyDeclaredException( - "Method " + methodNode.getIdentifier() + " is already defined in class " - + currentClass.identifier)); - valid = false; - } + for (var otherMethod : currentClass.getMethods()) { + if (Objects.equals(otherMethod, methodNode)) + break; + if (otherMethod.isSame(methodNode)) { + errors.add(new AlreadyDeclaredException( + "Method " + methodNode.getIdentifier() + " 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(); - try { - currentScope.addLocalVar(parameter.identifier, parameter.type); - } catch (AlreadyDeclaredException e) { - errors.add(new AlreadyDeclaredException(parameter.identifier)); - } - - } - - currentMethodReturnType = methodNode.getType(); - currentNullType = currentMethodReturnType; - - var result = methodNode.block.accept(this); + currentScope.pushScope(); + for (var parameter : methodNode.getParameters()) { + var result = parameter.accept(this); valid = valid && result.isValid(); - currentScope.popScope(); - ITypeNode resultType = result.getType(); - - if (resultType == null) { - resultType = new BaseType(TypeEnum.VOID); + try { + currentScope.addLocalVar(parameter.identifier, parameter.type); + } catch (AlreadyDeclaredException e) { + errors.add(new AlreadyDeclaredException(parameter.identifier)); } - if (methodNode.getType() == null) { - methodNode.setType(new BaseType(TypeEnum.VOID)); - } - - return new TypeCheckResult(valid, resultType); } + + currentMethodReturnType = methodNode.getType(); + currentNullType = currentMethodReturnType; + + var result = methodNode.block.accept(this); + valid = valid && result.isValid(); + currentScope.popScope(); + ITypeNode resultType = result.getType(); + + if (resultType == null) { + resultType = new BaseType(TypeEnum.VOID); + } + if (methodNode.getType() == null) { + methodNode.setType(new BaseType(TypeEnum.VOID)); + } + + return new TypeCheckResult(valid, resultType); + } @Override @@ -224,7 +220,7 @@ public class SemanticAnalyzer implements SemanticVisitor { } for (IStatementNode statementNode : blockNode.statements) { var result = statementNode.accept(this); - if(!(statementNode instanceof IncrementNode) && !(statementNode instanceof DecrementNode)){ + if (!(statementNode instanceof IncrementNode) && !(statementNode instanceof DecrementNode)) { if (result.getType() != null) { if (blockReturnType == null) { blockReturnType = result.getType(); @@ -250,6 +246,9 @@ public class SemanticAnalyzer implements SemanticVisitor { if (currentFields.get(toCheck.identifier) != null) { var type = currentFields.get(toCheck.identifier); toCheck.setTypeNode(type); + MemberAccessNode memberAccessNode = new MemberAccessNode(false); + memberAccessNode.identifiers.add(currentClass.identifier); + toCheck.memberAccess = memberAccessNode; return new TypeCheckResult(true, type); } else if (currentScope.getLocalVar(toCheck.identifier) != null) { var type = currentScope.getLocalVar(toCheck.identifier); @@ -409,6 +408,7 @@ public class SemanticAnalyzer implements SemanticVisitor { case PLUS, MINUS: if (calcRes.getType() instanceof BaseType calcType && dotRes.getType() instanceof BaseType dotType && calcType.getTypeEnum().equals(TypeEnum.INT) && dotType.getTypeEnum().equals(TypeEnum.INT)) { + calcNode.setType(new BaseType(TypeEnum.INT)); return new TypeCheckResult(true, new BaseType(TypeEnum.INT)); } break; @@ -416,10 +416,12 @@ public class SemanticAnalyzer implements SemanticVisitor { } } else { + calcNode.setType(calcRes.getType()); return new TypeCheckResult(calcRes.isValid(), calcRes.getType()); } } else if (calcNode.dotExpression != null) { var dotRes = calcNode.dotExpression.accept(this); + calcNode.setType(dotRes.getType()); return new TypeCheckResult(dotRes.isValid(), dotRes.getType()); } return new TypeCheckResult(false, null); @@ -462,6 +464,7 @@ public class SemanticAnalyzer implements SemanticVisitor { case LESS, LESS_EQUAL, GREATER, GREATER_EQUAL: if (expResult.getType() instanceof BaseType expResultType && expResultType.getTypeEnum().equals(TypeEnum.INT) && unaryResult.getType() instanceof BaseType unaryResultType && unaryResultType.getTypeEnum().equals(TypeEnum.INT)) { + nonCalculationNode.setType(new BaseType(TypeEnum.BOOL)); return new TypeCheckResult(true, new BaseType(TypeEnum.BOOL)); } else { errors.add(new TypeMismatchException("Both types must be Integer")); @@ -470,6 +473,7 @@ public class SemanticAnalyzer implements SemanticVisitor { case OR, AND: if (expResult.getType() instanceof BaseType expResultType && expResultType.getTypeEnum().equals(TypeEnum.INT) && unaryResult.getType() instanceof BaseType unaryResultType && unaryResultType.getTypeEnum().equals(TypeEnum.INT)) { + nonCalculationNode.setType(new BaseType(TypeEnum.BOOL)); return new TypeCheckResult(true, new BaseType(TypeEnum.BOOL)); } else { errors.add(new TypeMismatchException("Both types must be Boolean")); @@ -478,6 +482,7 @@ public class SemanticAnalyzer implements SemanticVisitor { case EQUAL, NOT_EQUAL: if (expResult.getType() instanceof BaseType expResultType && unaryResult.getType() instanceof BaseType unaryResultType && Objects.equals(expResultType, unaryResultType)) { + nonCalculationNode.setType(new BaseType(TypeEnum.BOOL)); return new TypeCheckResult(true, new BaseType(TypeEnum.BOOL)); } else { errors.add(new TypeMismatchException("Both types must be the same")); @@ -493,9 +498,13 @@ public class SemanticAnalyzer implements SemanticVisitor { if (unary.identifier != null) { if (currentScope.contains(unary.identifier)) { - return new TypeCheckResult(valid, currentScope.getLocalVar(unary.identifier)); + var type = currentScope.getLocalVar(unary.identifier); + unary.setType(type); + return new TypeCheckResult(valid, type); } else if (currentFields.get(unary.identifier) != null) { - return new TypeCheckResult(valid, currentFields.get(unary.identifier)); + var type = currentFields.get(unary.identifier); + unary.setType(type); + return new TypeCheckResult(valid, type); } else if (unary.statement != null) { var result = unary.statement.accept(this); unary.setType(result.getType()); @@ -505,15 +514,19 @@ public class SemanticAnalyzer implements SemanticVisitor { } } else if (unary.statement != null) { var result = unary.statement.accept(this); + unary.setType(result.getType()); return new TypeCheckResult(result.isValid(), result.getType()); } else if (unary.value != null) { var result = unary.value.accept(this); + unary.setType(result.getType()); return new TypeCheckResult(result.isValid(), result.getType()); } else if (unary.memberAccess != null) { var result = unary.memberAccess.accept(this); + unary.setType(result.getType()); return new TypeCheckResult(result.isValid(), result.getType()); } else if (unary.expression != null) { var result = unary.expression.accept(this); + unary.setType(result.getType()); return new TypeCheckResult(result.isValid(), result.getType()); } @@ -525,6 +538,10 @@ public class SemanticAnalyzer implements SemanticVisitor { ITypeNode currentType = null; + if (memberAccessNode.thisExpr) { + currentType = new ReferenceType(currentClass.identifier); + } + for (String s : memberAccessNode.identifiers) { if (currentType == null) { if (currentScope.getLocalVar(s) != null) { @@ -542,7 +559,7 @@ public class SemanticAnalyzer implements SemanticVisitor { var currentTypeClass = context.getClass(reference.getIdentifier()); var currentField = currentTypeClass.getField(s); - if (currentField.getAccessModifier().accessType == EnumAccessModifierNode.PUBLIC) { + if (currentField.getAccessModifier().accessType == EnumAccessModifierNode.PUBLIC || memberAccessNode.thisExpr) { currentType = currentField.getType(); } else { errors.add(new NotVisibleException("This field is not visible")); diff --git a/src/main/java/semantic/context/FieldContext.java b/src/main/java/semantic/context/FieldContext.java index aba5ba0..3869bb1 100644 --- a/src/main/java/semantic/context/FieldContext.java +++ b/src/main/java/semantic/context/FieldContext.java @@ -4,13 +4,15 @@ import ast.members.FieldNode; import ast.type.*; import ast.type.type.*; +import java.util.Objects; + public class FieldContext { private AccessModifierNode accessModifier; private ITypeNode type; public FieldContext(FieldNode field) { - accessModifier = field.accessTypeNode; + accessModifier = Objects.requireNonNullElseGet(field.accessTypeNode, () -> new AccessModifierNode("private")); type = field.type; } diff --git a/src/test/resources/input/typedAstExceptionsTests/DuplicatedConstructor.java b/src/test/resources/input/typedAstExceptionsTests/DuplicatedConstructor.java new file mode 100644 index 0000000..3d877a3 --- /dev/null +++ b/src/test/resources/input/typedAstExceptionsTests/DuplicatedConstructor.java @@ -0,0 +1,12 @@ +// @expected: AlreadyDeclaredException +public class AllFeaturesClassExample { + + public AllFeaturesClassExample(boolean b){ + + } + + public AllFeaturesClassExample(boolean b){ + + } + +} diff --git a/src/test/resources/input/typedAstExceptionsTests/FieldOrParameterTypeMismatch.java b/src/test/resources/input/typedAstExceptionsTests/FieldOrParameterTypeMismatch.java new file mode 100644 index 0000000..0a9c17c --- /dev/null +++ b/src/test/resources/input/typedAstExceptionsTests/FieldOrParameterTypeMismatch.java @@ -0,0 +1,10 @@ +// @expected: TypeMismatchException +public class AllFeaturesClassExample { + int x; + + public boolean test(boolean x){ + return this.x; + } + +} + diff --git a/src/test/resources/input/typedAstFeaturesTests/ConstructorOverloading.java b/src/test/resources/input/typedAstFeaturesTests/ConstructorOverloading.java new file mode 100644 index 0000000..1e0bc49 --- /dev/null +++ b/src/test/resources/input/typedAstFeaturesTests/ConstructorOverloading.java @@ -0,0 +1,11 @@ +public class AllFeaturesClassExample { + + public AllFeaturesClassExample(boolean b){ + + } + + public AllFeaturesClassExample(boolean b, int a){ + + } + +} diff --git a/src/test/resources/input/typedAstFeaturesTests/CorrectTest.java b/src/test/resources/input/typedAstFeaturesTests/CorrectTest.java index 5ef0b26..4d27ecd 100644 --- a/src/test/resources/input/typedAstFeaturesTests/CorrectTest.java +++ b/src/test/resources/input/typedAstFeaturesTests/CorrectTest.java @@ -1,38 +1,10 @@ + public class AllFeaturesClassExample { - int a; - boolean b; - char c; - - public void controlStructures(int adf, boolean bool) { - if (a > (10 + 8)) { - } else { - } - - - while (a > adf) { - a--; - } - - for (int i = 0; i < 5; i++) { - } - + int x; + public void test(){ + x = 1; } -// void logicalOperations() { - // Logische UND-Operation -// if (b && a > 5) { -// System.out.println("a ist größer als 5 und b ist wahr"); -// } - - // Logische ODER-Operation -// if (b || a < 5) { -// System.out.println("b ist wahr oder a ist kleiner als 5"); -// } -// } - -// public static void main(String[] args) { -// AllFeaturesClassExample obj = new AllFeaturesClassExample(12, true, 'a'); -// obj.controlStructures(); -// } } +