diff --git a/src/main/java/de/maishai/ast/Node.java b/src/main/java/de/maishai/ast/Node.java index b07e221..250bab9 100644 --- a/src/main/java/de/maishai/ast/Node.java +++ b/src/main/java/de/maishai/ast/Node.java @@ -1,4 +1,11 @@ package de.maishai.ast; +import de.maishai.typedast.Type; +import de.maishai.ast.records.Class; +import java.util.Map; + public interface Node { + + public Type typeCheck(Map localVars, Map classes); + } diff --git a/src/main/java/de/maishai/ast/Statement.java b/src/main/java/de/maishai/ast/Statement.java index c9e690c..2d38f50 100644 --- a/src/main/java/de/maishai/ast/Statement.java +++ b/src/main/java/de/maishai/ast/Statement.java @@ -1,3 +1,4 @@ package de.maishai.ast; public interface Statement extends Node { + } diff --git a/src/main/java/de/maishai/ast/Type.java b/src/main/java/de/maishai/ast/Type.java deleted file mode 100644 index 8e1f050..0000000 --- a/src/main/java/de/maishai/ast/Type.java +++ /dev/null @@ -1,7 +0,0 @@ -package de.maishai.ast; - -public enum Type { - INT, - BOOL, - CHAR -} diff --git a/src/main/java/de/maishai/ast/records/Assignment.java b/src/main/java/de/maishai/ast/records/Assignment.java index a5c596b..33d5f14 100644 --- a/src/main/java/de/maishai/ast/records/Assignment.java +++ b/src/main/java/de/maishai/ast/records/Assignment.java @@ -3,6 +3,20 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; import de.maishai.ast.Statement; +import de.maishai.typedast.Type; + +import java.util.Map; public record Assignment(Id loc, Expression value) implements Statement, Expression { + @Override + public Type typeCheck(Map localVars, Map classes) { + + if (localVars.get(loc.name()) == null) { + throw new RuntimeException("Variable " + loc.name() + " not declared"); + } + if (!localVars.get(loc.name()).equals(value.typeCheck(localVars, classes))) { + throw new RuntimeException("Type mismatch in assignment to " + loc.name()); + } + return loc.typeCheck(localVars, classes); + } } diff --git a/src/main/java/de/maishai/ast/records/Binary.java b/src/main/java/de/maishai/ast/records/Binary.java index 40070e3..e1fe1f2 100644 --- a/src/main/java/de/maishai/ast/records/Binary.java +++ b/src/main/java/de/maishai/ast/records/Binary.java @@ -3,6 +3,17 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; import de.maishai.ast.Operator; +import de.maishai.typedast.Type; + +import java.util.Map; public record Binary(Expression left, Operator op, Expression right) implements Expression { + @Override + public Type typeCheck(Map localVars, Map classes) { + // x = y + z -> y, z : int + if(left.typeCheck(localVars, classes) != Type.INT && right.typeCheck(localVars, classes) != Type.INT) { + throw new RuntimeException("Binary operation on non-integers"); + } + return Type.INT; + } } diff --git a/src/main/java/de/maishai/ast/records/Block.java b/src/main/java/de/maishai/ast/records/Block.java index e36affe..eb0f6ef 100644 --- a/src/main/java/de/maishai/ast/records/Block.java +++ b/src/main/java/de/maishai/ast/records/Block.java @@ -4,8 +4,23 @@ package de.maishai.ast.records; import de.maishai.ast.Node; import de.maishai.ast.Statement; +import de.maishai.typedast.Type; import java.util.List; +import java.util.Map; public record Block(List vars, List stmts) implements Node { + @Override + public Type typeCheck(Map localVars, Map classes) { + // Add the variables to the local variables + for (Variable var : vars) { + localVars.put(var.name(), var.type()); + } + // Type check the statements + for (Statement stmt : stmts) { + stmt.typeCheck(localVars, classes); + } + // Return the type of the last statement + return stmts.get(stmts.size() - 1).typeCheck(localVars, classes); + } } diff --git a/src/main/java/de/maishai/ast/records/BoolConstant.java b/src/main/java/de/maishai/ast/records/BoolConstant.java index 3b6e0a9..e9f4014 100644 --- a/src/main/java/de/maishai/ast/records/BoolConstant.java +++ b/src/main/java/de/maishai/ast/records/BoolConstant.java @@ -2,6 +2,13 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; +import de.maishai.typedast.Type; + +import java.util.Map; public record BoolConstant(Boolean value) implements Expression { + @Override + public Type typeCheck(Map localVars, Map classes) { + return Type.BOOL; + } } diff --git a/src/main/java/de/maishai/ast/records/Break.java b/src/main/java/de/maishai/ast/records/Break.java index 9e9dc87..dfa3add 100644 --- a/src/main/java/de/maishai/ast/records/Break.java +++ b/src/main/java/de/maishai/ast/records/Break.java @@ -2,6 +2,13 @@ package de.maishai.ast.records; import de.maishai.ast.Statement; +import de.maishai.typedast.Type; + +import java.util.Map; public record Break() implements Statement { + @Override + public Type typeCheck(Map localVars, Map classes) { + return Type.VOID; + } } diff --git a/src/main/java/de/maishai/ast/records/CharConstant.java b/src/main/java/de/maishai/ast/records/CharConstant.java index fdf4ec6..ea87871 100644 --- a/src/main/java/de/maishai/ast/records/CharConstant.java +++ b/src/main/java/de/maishai/ast/records/CharConstant.java @@ -2,6 +2,13 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; +import de.maishai.typedast.Type; + +import java.util.Map; public record CharConstant(char value) implements Expression { + @Override + public Type typeCheck(Map localVars, Map classes) { + return Type.CHAR; + } } diff --git a/src/main/java/de/maishai/ast/records/Class.java b/src/main/java/de/maishai/ast/records/Class.java index 203a48e..bd80e23 100644 --- a/src/main/java/de/maishai/ast/records/Class.java +++ b/src/main/java/de/maishai/ast/records/Class.java @@ -1,8 +1,25 @@ package de.maishai.ast.records; import de.maishai.ast.Node; +import de.maishai.typedast.Type; import java.util.List; +import java.util.Map; public record Class(Id id ,List variables, List methods) implements Node { + @Override + public Type typeCheck(Map localVars, Map classes) { + if(classes.containsKey(id.name())) { + throw new RuntimeException("Class " + id.name() + " already declared"); + } + classes.put(id.name(), this); + for(Variable variable : variables) { + variable.typeCheck(localVars, classes); + } + for(Method method : methods) { + method.typeCheck(localVars, classes); + } + + return Type.VOID; + } } diff --git a/src/main/java/de/maishai/ast/records/Continue.java b/src/main/java/de/maishai/ast/records/Continue.java index 29b7382..2f9141c 100644 --- a/src/main/java/de/maishai/ast/records/Continue.java +++ b/src/main/java/de/maishai/ast/records/Continue.java @@ -2,6 +2,13 @@ package de.maishai.ast.records; import de.maishai.ast.Statement; +import de.maishai.typedast.Type; + +import java.util.Map; public record Continue() implements Statement { + @Override + public Type typeCheck(Map localVars, Map classes) { + return Type.VOID; + } } diff --git a/src/main/java/de/maishai/ast/records/For.java b/src/main/java/de/maishai/ast/records/For.java index 2440947..0e1aea1 100644 --- a/src/main/java/de/maishai/ast/records/For.java +++ b/src/main/java/de/maishai/ast/records/For.java @@ -3,6 +3,21 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; import de.maishai.ast.Statement; +import de.maishai.typedast.Type; + +import java.util.Map; public record For(Expression assign, Expression cond, Expression inc, Block block) implements Statement { + @Override + public Type typeCheck(Map localVars, Map classes) { + // like int i = 0 + assign.typeCheck(localVars, classes); + // like i < 10 -> bool + if (cond.typeCheck(localVars, classes) != Type.BOOL) { + throw new RuntimeException("Condition must be of type bool"); + } + // like i++ + inc.typeCheck(localVars, classes); + return block.typeCheck(localVars, classes); + } } diff --git a/src/main/java/de/maishai/ast/records/Id.java b/src/main/java/de/maishai/ast/records/Id.java index 5e5f3ea..57599fa 100644 --- a/src/main/java/de/maishai/ast/records/Id.java +++ b/src/main/java/de/maishai/ast/records/Id.java @@ -2,6 +2,16 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; +import de.maishai.typedast.Type; -public record Id(String name) implements Expression { -} +import java.util.Map; + + public record Id(String name) implements Expression { + @Override + public Type typeCheck(Map localVars, Map classes) { + if(!localVars.containsKey(name)) { + throw new RuntimeException("Variable " + name + " not declared"); + } + return localVars.get(name); + } + } diff --git a/src/main/java/de/maishai/ast/records/IfElse.java b/src/main/java/de/maishai/ast/records/IfElse.java index 50c355b..e867f11 100644 --- a/src/main/java/de/maishai/ast/records/IfElse.java +++ b/src/main/java/de/maishai/ast/records/IfElse.java @@ -2,5 +2,21 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; import de.maishai.ast.Statement; +import de.maishai.typedast.Type; + +import java.util.Map; + public record IfElse(Expression cond, Block ifBlock, Block elseBlock) implements Statement { + + @Override + public Type typeCheck(Map localVars, Map classes) { + + // Con must be type bool and both blocks must have the same type + if(cond.typeCheck(localVars, classes).equals(Type.BOOL) + && ifBlock.typeCheck(localVars, classes).equals(elseBlock.typeCheck(localVars, classes))) { + return ifBlock.typeCheck(localVars, classes); + } + + throw new RuntimeException("IfElse statement type check failed"); + } } diff --git a/src/main/java/de/maishai/ast/records/IntConstant.java b/src/main/java/de/maishai/ast/records/IntConstant.java index 79c1ea7..c049b94 100644 --- a/src/main/java/de/maishai/ast/records/IntConstant.java +++ b/src/main/java/de/maishai/ast/records/IntConstant.java @@ -2,6 +2,13 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; +import de.maishai.typedast.Type; + +import java.util.Map; public record IntConstant(Integer value) implements Expression { + @Override + public Type typeCheck(Map localVars, Map classes) { + return Type.INT; + } } diff --git a/src/main/java/de/maishai/ast/records/MainMethod.java b/src/main/java/de/maishai/ast/records/MainMethod.java index 0da1915..bbb5335 100644 --- a/src/main/java/de/maishai/ast/records/MainMethod.java +++ b/src/main/java/de/maishai/ast/records/MainMethod.java @@ -4,8 +4,15 @@ package de.maishai.ast.records; import de.maishai.ast.Node; import de.maishai.ast.ReturnType; +import de.maishai.typedast.Type; import java.util.List; +import java.util.Map; public record MainMethod(ReturnType type, Id id, List params, Block block) implements Node { + + @Override + public Type typeCheck(Map localVars, Map classes) { + return null; + } } diff --git a/src/main/java/de/maishai/ast/records/Method.java b/src/main/java/de/maishai/ast/records/Method.java index 742bb54..7bfb8f6 100644 --- a/src/main/java/de/maishai/ast/records/Method.java +++ b/src/main/java/de/maishai/ast/records/Method.java @@ -1,11 +1,32 @@ package de.maishai.ast.records; - import de.maishai.ast.Node; import de.maishai.ast.ReturnType; +import de.maishai.typedast.Type; import java.util.List; +import java.util.Map; -public record Method(ReturnType type, Id id, List params, Block block) implements Node { +public record Method(Type type, Id id, List params, Block block) implements Node { + + @Override + public Type typeCheck(Map localVars, Map classes) { + if (localVars.containsKey(id.name())) { + throw new RuntimeException("Variable " + id.name() + " already declared"); + } + localVars.put(id.name(), type); + + for (Parameter parameter : params) { + parameter.typeCheck(localVars, classes); + } + + Type returnType = block.typeCheck(localVars, classes); + if (!returnType.equals(type)) { + throw new RuntimeException("Return type mismatch"); + } + + return returnType; + + } } diff --git a/src/main/java/de/maishai/ast/records/MethodCall.java b/src/main/java/de/maishai/ast/records/MethodCall.java index 2f84949..265b356 100644 --- a/src/main/java/de/maishai/ast/records/MethodCall.java +++ b/src/main/java/de/maishai/ast/records/MethodCall.java @@ -4,8 +4,49 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; import de.maishai.ast.Statement; +import de.maishai.typedast.Type; import java.util.List; +import java.util.Map; +import java.util.Optional; + +public record MethodCall(Optional receiver, String name, List args) implements Expression, Statement { + + @Override + public Type typeCheck(Map localVars, Map classes) { + Type returnType = null; + + if(receiver.isPresent()) { + // check if receiver is a local variable + if(!localVars.containsKey(receiver.get())){ + throw new RuntimeException("Variable " + receiver.get() + " not found"); + } + } + + // check if method exists in class + if(localVars.get(name) == null) { + throw new RuntimeException("Variable " + name + " not found"); + } + + Type type = localVars.get(name); + Class class1 = classes.get(type.name()); + if(class1 == null) { + throw new RuntimeException("Class " + type + " not found"); + } + for(Method method : class1.methods()){ + if(method.id().name().equals(name)){ + for(int i = 0; i < method.params().size(); i++){ + if(!method.params().get(i).type().equals(args.get(i).typeCheck(localVars, classes))){ + throw new RuntimeException("Type mismatch in method call " + name); + } + } + returnType = method.type(); + break; + }else{ + throw new RuntimeException("Method " + name + " not found in class " + type); + } + } + return returnType; + } -public record MethodCall(String name, List args) implements Expression, Statement { } diff --git a/src/main/java/de/maishai/ast/records/New.java b/src/main/java/de/maishai/ast/records/New.java index 347fb7d..2acece2 100644 --- a/src/main/java/de/maishai/ast/records/New.java +++ b/src/main/java/de/maishai/ast/records/New.java @@ -1,12 +1,18 @@ package de.maishai.ast.records; - import de.maishai.ast.Expression; + import de.maishai.ast.Statement; -import de.maishai.ast.Type; +import de.maishai.typedast.Type; import java.util.List; +import java.util.Map; public record New(Type type, List args) implements Expression, Statement { + + @Override + public Type typeCheck(Map localVars, Map classes) { + return type; + } } diff --git a/src/main/java/de/maishai/ast/records/Parameter.java b/src/main/java/de/maishai/ast/records/Parameter.java index 508ef1e..8952fcd 100644 --- a/src/main/java/de/maishai/ast/records/Parameter.java +++ b/src/main/java/de/maishai/ast/records/Parameter.java @@ -2,7 +2,18 @@ package de.maishai.ast.records; import de.maishai.ast.Node; -import de.maishai.ast.Type; +import de.maishai.typedast.Type; + +import java.util.Map; public record Parameter(String name, Type type) implements Node { + + @Override + public Type typeCheck(Map localVars, Map classes) { + if (localVars.containsKey(name)) { + throw new RuntimeException("Variable " + name + " already declared"); + } + localVars.put(name, type); + return type; + } } diff --git a/src/main/java/de/maishai/ast/records/Program.java b/src/main/java/de/maishai/ast/records/Program.java index ec638c0..6952d2e 100644 --- a/src/main/java/de/maishai/ast/records/Program.java +++ b/src/main/java/de/maishai/ast/records/Program.java @@ -2,8 +2,14 @@ package de.maishai.ast.records; import de.maishai.ast.Node; +import de.maishai.typedast.Type; import java.util.List; +import java.util.Map; public record Program(List variables) implements Node { + @Override + public Type typeCheck(Map localVars, Map classes) { + return null; + } } diff --git a/src/main/java/de/maishai/ast/records/Return.java b/src/main/java/de/maishai/ast/records/Return.java index 5e3e0bd..4e8330e 100644 --- a/src/main/java/de/maishai/ast/records/Return.java +++ b/src/main/java/de/maishai/ast/records/Return.java @@ -3,6 +3,15 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; import de.maishai.ast.Statement; +import de.maishai.typedast.Type; + +import java.util.Map; + public record Return(Expression ret) implements Statement { + + @Override + public Type typeCheck(Map localVars, Map classes) { + return ret.typeCheck(localVars, classes); + } } diff --git a/src/main/java/de/maishai/ast/records/ReturnVoid.java b/src/main/java/de/maishai/ast/records/ReturnVoid.java index 7100004..1b2d662 100644 --- a/src/main/java/de/maishai/ast/records/ReturnVoid.java +++ b/src/main/java/de/maishai/ast/records/ReturnVoid.java @@ -2,6 +2,14 @@ package de.maishai.ast.records; import de.maishai.ast.Statement; +import de.maishai.typedast.Type; +import java.util.Map; + +// Why does ReturnVoid not have statements public record ReturnVoid() implements Statement { + @Override + public Type typeCheck(Map localVars, Map classes) { + return Type.VOID; + } } diff --git a/src/main/java/de/maishai/ast/records/Variable.java b/src/main/java/de/maishai/ast/records/Variable.java index 1f2901c..791b339 100644 --- a/src/main/java/de/maishai/ast/records/Variable.java +++ b/src/main/java/de/maishai/ast/records/Variable.java @@ -2,7 +2,18 @@ package de.maishai.ast.records; import de.maishai.ast.Node; -import de.maishai.ast.Type; +import de.maishai.typedast.Type; + +import java.util.Map; public record Variable(String name, Type type) implements Node { + + @Override + public Type typeCheck(Map localVars, Map classes) { + if (localVars.containsKey(name)) { + throw new RuntimeException("Variable " + name + " already declared"); + } + localVars.put(name, type); + return type; + } } diff --git a/src/main/java/de/maishai/ast/records/While.java b/src/main/java/de/maishai/ast/records/While.java index 47b7548..d48c6f2 100644 --- a/src/main/java/de/maishai/ast/records/While.java +++ b/src/main/java/de/maishai/ast/records/While.java @@ -3,6 +3,19 @@ package de.maishai.ast.records; import de.maishai.ast.Expression; import de.maishai.ast.Statement; +import de.maishai.typedast.Type; + +import java.util.Map; public record While(Expression cond, Block block) implements Statement { + + + @Override + public Type typeCheck(Map localVars, Map classes) { + Type condType = cond.typeCheck(localVars, classes); + if (condType != Type.BOOL) { + throw new RuntimeException("Condition must be of type bool"); + } + return block.typeCheck(localVars, classes); + } }