diff --git a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 index efe054d..f752ddb 100644 --- a/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 +++ b/src/main/antlr4/de/hsrm/compiler/Klang/Klang.g4 @@ -33,7 +33,7 @@ parameter ; braced_block - : OBRK statement+ CBRK + : OBRK (statement | functionCall SCOL)+ CBRK ; @@ -73,7 +73,7 @@ field_assignment ; return_statement - : RETURN expression SCOL + : RETURN expression? SCOL ; destroy_statement @@ -120,6 +120,7 @@ type | BOOLEAN | FLOAT | IDENT + | VOID ; functionCall @@ -186,6 +187,7 @@ DIV: '/'; BOOLEAN: 'bool'; INTEGER: 'int'; FLOAT: 'float'; +VOID: 'void'; INTEGER_LITERAL : [0-9]+ diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index a2323a9..982237e 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -186,6 +186,11 @@ public class ContextAnalysis extends KlangBaseVisitor { var line = ctx.start.getLine(); var col = ctx.start.getCharPositionInLine(); + if (declaredType.equals(Type.getVoidType())) { + var error = "Type " + declaredType.getName() + " can not be used to declare variables."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } + if (!declaredType.isPrimitiveType() && !structDefs.containsKey(declaredType.getName()) && !enumDefs.containsKey(declaredType.getName())) { var error = "Type " + declaredType.getName() + " not defined."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); @@ -266,6 +271,18 @@ public class ContextAnalysis extends KlangBaseVisitor { @Override public Node visitReturn_statement(KlangParser.Return_statementContext ctx) { + if (currentDeclaredReturnType.equals(Type.getVoidType())) { + ReturnStatement result = new ReturnStatement(); + result.type = Type.getVoidType(); + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); + if (ctx.expression() != null) { + String error = "Cannot return an expression from a void function."; + throw new RuntimeException(Helper.getErrorPrefix(result.line, result.col) + error); + } + return result; + } + Expression expression = (Expression) this.visit(ctx.expression()); ReturnStatement result = new ReturnStatement(expression); @@ -277,8 +294,8 @@ public class ContextAnalysis extends KlangBaseVisitor { } } - result.type = expression.type; result.line = ctx.start.getLine(); + result.type = expression.type; result.col = ctx.start.getCharPositionInLine(); return result; } @@ -826,26 +843,29 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitParameter(KlangParser.ParameterContext ctx) { var parameterName = ctx.IDENT().getText(); var parameterType = Type.getByName(ctx.type_annotation().type().getText()); + var line = ctx.start.getLine(); + var col = ctx.start.getCharPositionInLine(); + + if (parameterType.equals(Type.getVoidType())) { + var error = "Type " + parameterType.getName() + " cannot be used to declare a parameter."; + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + } if (structDefs.containsKey(parameterName)) { - var line = ctx.start.getLine(); - var col = ctx.start.getCharPositionInLine(); var error = "Parameter name " + parameterName + " duplicates a struct of the same name."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } if (enumDefs.containsKey(parameterName)) { - var line = ctx.start.getLine(); - var col = ctx.start.getCharPositionInLine(); var error = "Parameter name " + parameterName + " duplicates an enum of the same name."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } if (!parameterType.isPrimitiveType() && !structDefs.containsKey(parameterType.getName()) && !enumDefs.containsKey(parameterType.getName())) { - var line = ctx.type_annotation().start.getLine(); - var col = ctx.type_annotation().start.getCharPositionInLine(); + var typeLine = ctx.type_annotation().start.getLine(); + var typeCol = ctx.type_annotation().start.getCharPositionInLine(); var error = "Type " + parameterType.getName() + " not defined."; - throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + throw new RuntimeException(Helper.getErrorPrefix(typeLine, typeCol) + error); } var parameter = new Parameter(parameterName); @@ -983,4 +1003,4 @@ public class ContextAnalysis extends KlangBaseVisitor { } return path; } -} \ No newline at end of file +} diff --git a/src/main/java/de/hsrm/compiler/Klang/Klang.java b/src/main/java/de/hsrm/compiler/Klang/Klang.java index ca8094c..efa84fe 100644 --- a/src/main/java/de/hsrm/compiler/Klang/Klang.java +++ b/src/main/java/de/hsrm/compiler/Klang/Klang.java @@ -1,9 +1,11 @@ package de.hsrm.compiler.Klang; +import de.hsrm.compiler.Klang.helper.*; import de.hsrm.compiler.Klang.nodes.EnumDefinition; import de.hsrm.compiler.Klang.nodes.FunctionDefinition; import de.hsrm.compiler.Klang.nodes.Node; import de.hsrm.compiler.Klang.nodes.StructDefinition; +import de.hsrm.compiler.Klang.types.Type; import de.hsrm.compiler.Klang.visitors.EvalVisitor; import de.hsrm.compiler.Klang.visitors.GenASM; import de.hsrm.compiler.Klang.visitors.PrettyPrintVisitor; @@ -120,7 +122,11 @@ public class Klang { System.out.println("\nEvaluating the source code:"); EvalVisitor evalVisitor = new EvalVisitor(structDefs); Value result = root.welcome(evalVisitor); - generateOutput(out, "Result was: " + result.asObject().toString()); + if (result.type.equals(Type.getVoidType())) { + generateOutput(out, "Result was void"); + } else { + generateOutput(out, "Result was: " + result.asObject().toString()); + } return; } diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/statements/ReturnStatement.java b/src/main/java/de/hsrm/compiler/Klang/nodes/statements/ReturnStatement.java index ef37a52..e98ddf7 100644 --- a/src/main/java/de/hsrm/compiler/Klang/nodes/statements/ReturnStatement.java +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/statements/ReturnStatement.java @@ -11,6 +11,10 @@ public class ReturnStatement extends Statement { this.expression = expression; } + public ReturnStatement() { + this.expression = null; + } + @Override public R welcome(Visitor v) { return v.visit(this); diff --git a/src/main/java/de/hsrm/compiler/Klang/types/Type.java b/src/main/java/de/hsrm/compiler/Klang/types/Type.java index 4ce91ca..a8261df 100644 --- a/src/main/java/de/hsrm/compiler/Klang/types/Type.java +++ b/src/main/java/de/hsrm/compiler/Klang/types/Type.java @@ -27,12 +27,17 @@ public abstract class Type { return NullType.getType(); } + public static VoidType getVoidType() { + return VoidType.getType(); + } + public static Type getByName(String name) { switch (name) { case "bool": return getBooleanType(); case "int": return getIntegerType(); case "float": return getFloatType(); case "null": return getNullType(); + case "void": return getVoidType(); default: return new NamedType(name); } } @@ -42,4 +47,4 @@ public abstract class Type { public abstract boolean valuesEqual(Value a, Value b); public abstract boolean isPrimitiveType(); public abstract boolean isNumericType(); -} \ No newline at end of file +} diff --git a/src/main/java/de/hsrm/compiler/Klang/types/VoidType.java b/src/main/java/de/hsrm/compiler/Klang/types/VoidType.java new file mode 100644 index 0000000..77730c9 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/types/VoidType.java @@ -0,0 +1,45 @@ +package de.hsrm.compiler.Klang.types; + +import de.hsrm.compiler.Klang.Value; + +public class VoidType extends Type { + + private static VoidType instance; + + public static VoidType getType() { + if (instance != null) { + return instance; + } + instance = new VoidType(); + return instance; +} + + @Override + public String getName() { + return "void"; + } + + @Override + public Type combine(Type that) { + if (that.equals(this)) { + return this; + } + throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName()); + } + + @Override + public boolean valuesEqual(Value a, Value b) { + throw new RuntimeException("Can not compare void types."); + } + + @Override + public boolean isPrimitiveType() { + return false; + } + + @Override + public boolean isNumericType() { + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java index c1b93ee..ee6193a 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java @@ -396,6 +396,9 @@ public class EvalVisitor implements Visitor { @Override public Value visit(ReturnStatement e) { + if (e.expression == null) { + return new Value(null, Type.getVoidType()); + } return e.expression.welcome(this); } diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java index c02db48..4618c3d 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GenASM.java @@ -525,7 +525,9 @@ public class GenASM implements Visitor { @Override public Void visit(ReturnStatement e) { - e.expression.welcome(this); + if (e.expression != null) { + e.expression.welcome(this); + } // The ReturnStatement visitor is kindly removing the // stack space that was allocated by the FunctionDefinition @@ -1020,4 +1022,4 @@ public class GenASM implements Visitor { return false; } } -} \ No newline at end of file +} diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java b/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java index 33e6b6b..45220bc 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/GetVars.java @@ -194,7 +194,9 @@ class GetVars implements Visitor { @Override public Void visit(ReturnStatement e) { - e.expression.welcome(this); + if (e.expression != null) { + e.expression.welcome(this); + } return null; } diff --git a/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java b/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java index 252797b..470e3dd 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/PrettyPrintVisitor.java @@ -310,8 +310,11 @@ public class PrettyPrintVisitor implements Visitor { @Override public Void visit(ReturnStatement e) { - ex.write("return "); - e.expression.welcome(this); + ex.write("return"); + if (e.expression != null) { + ex.write(" "); + e.expression.welcome(this); + } ex.write(";"); return null; }