Compare commits
4 Commits
c124587983
...
4219f93021
| Author | SHA1 | Date | |
|---|---|---|---|
| 4219f93021 | |||
| 7965c89a60 | |||
| 26eff47057 | |||
|
|
53976615e1 |
@@ -33,7 +33,7 @@ parameter
|
|||||||
;
|
;
|
||||||
|
|
||||||
braced_block
|
braced_block
|
||||||
: OBRK statement+ CBRK
|
: OBRK (statement | functionCall SCOL)+ CBRK
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
||||||
@@ -73,7 +73,7 @@ field_assignment
|
|||||||
;
|
;
|
||||||
|
|
||||||
return_statement
|
return_statement
|
||||||
: RETURN expression SCOL
|
: RETURN expression? SCOL
|
||||||
;
|
;
|
||||||
|
|
||||||
destroy_statement
|
destroy_statement
|
||||||
@@ -120,6 +120,7 @@ type
|
|||||||
| BOOLEAN
|
| BOOLEAN
|
||||||
| FLOAT
|
| FLOAT
|
||||||
| IDENT
|
| IDENT
|
||||||
|
| VOID
|
||||||
;
|
;
|
||||||
|
|
||||||
functionCall
|
functionCall
|
||||||
@@ -186,6 +187,7 @@ DIV: '/';
|
|||||||
BOOLEAN: 'bool';
|
BOOLEAN: 'bool';
|
||||||
INTEGER: 'int';
|
INTEGER: 'int';
|
||||||
FLOAT: 'float';
|
FLOAT: 'float';
|
||||||
|
VOID: 'void';
|
||||||
|
|
||||||
INTEGER_LITERAL
|
INTEGER_LITERAL
|
||||||
: [0-9]+
|
: [0-9]+
|
||||||
|
|||||||
@@ -68,13 +68,13 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitBraced_block(KlangParser.Braced_blockContext ctx) {
|
public Node visitBraced_block(KlangParser.Braced_blockContext ctx) {
|
||||||
int actualStatementCount = 0;
|
var actualStatementCount = 0;
|
||||||
int declaredStatementCount = ctx.statement().size();
|
var declaredStatementCount = ctx.statement().size();
|
||||||
boolean hasReturn = false;
|
var hasReturn = false;
|
||||||
Statement[] statements = new Statement[declaredStatementCount];
|
var statements = new Statement[declaredStatementCount];
|
||||||
|
|
||||||
for (int i = 0; i < declaredStatementCount; i++) {
|
for (int i = 0; i < declaredStatementCount; i++) {
|
||||||
Node currentStatement = this.visit(ctx.statement(i));
|
var currentStatement = visit(ctx.statement(i));
|
||||||
statements[i] = (Statement) currentStatement;
|
statements[i] = (Statement) currentStatement;
|
||||||
actualStatementCount += 1;
|
actualStatementCount += 1;
|
||||||
|
|
||||||
@@ -83,7 +83,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) {
|
if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) {
|
||||||
// check whether the type matches
|
// check whether the type matches
|
||||||
try {
|
try {
|
||||||
this.currentDeclaredReturnType.combine(currentStatement.type);
|
currentDeclaredReturnType.combine(currentStatement.type);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(
|
throw new RuntimeException(
|
||||||
Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage());
|
Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage());
|
||||||
@@ -99,14 +99,15 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
// If there was unreachable code in this block,
|
// If there was unreachable code in this block,
|
||||||
// create a shorter statements array and copy the statements to there
|
// create a shorter statements array and copy the statements to there
|
||||||
if (actualStatementCount < declaredStatementCount) {
|
if (actualStatementCount < declaredStatementCount) {
|
||||||
Statement[] newStatements = new Statement[actualStatementCount];
|
var newStatements = new Statement[actualStatementCount];
|
||||||
System.arraycopy(statements, 0, newStatements, 0, actualStatementCount);
|
System.arraycopy(statements, 0, newStatements, 0, actualStatementCount);
|
||||||
statements = newStatements;
|
statements = newStatements;
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this block contains at least one statement that guarantees a return value,
|
// if this block contains at least one statement that guarantees a return value,
|
||||||
// we indicate that this block guarantees a return value by setting result.type
|
// we indicate that this block guarantees a return value by setting result.type
|
||||||
Block result = new Block(statements);
|
var result = new Block(statements);
|
||||||
|
|
||||||
if (hasReturn) {
|
if (hasReturn) {
|
||||||
result.type = this.currentDeclaredReturnType;
|
result.type = this.currentDeclaredReturnType;
|
||||||
}
|
}
|
||||||
@@ -186,6 +187,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
var line = ctx.start.getLine();
|
var line = ctx.start.getLine();
|
||||||
var col = ctx.start.getCharPositionInLine();
|
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())) {
|
if (!declaredType.isPrimitiveType() && !structDefs.containsKey(declaredType.getName()) && !enumDefs.containsKey(declaredType.getName())) {
|
||||||
var error = "Type " + declaredType.getName() + " not defined.";
|
var error = "Type " + declaredType.getName() + " not defined.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
@@ -266,19 +272,31 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Node visitReturn_statement(KlangParser.Return_statementContext ctx) {
|
public Node visitReturn_statement(KlangParser.Return_statementContext ctx) {
|
||||||
Expression expression = (Expression) this.visit(ctx.expression());
|
if (currentDeclaredReturnType.equals(Type.getVoidType())) {
|
||||||
ReturnStatement result = new ReturnStatement(expression);
|
var result = new ReturnStatement();
|
||||||
|
result.type = Type.getVoidType();
|
||||||
|
result.line = ctx.start.getLine();
|
||||||
|
result.col = ctx.start.getCharPositionInLine();
|
||||||
|
if (ctx.expression() != null) {
|
||||||
|
var error = "Cannot return an expression from a void function.";
|
||||||
|
throw new RuntimeException(Helper.getErrorPrefix(result.line, result.col) + error);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
var expression = (Expression) visit(ctx.expression());
|
||||||
|
|
||||||
// Check if this expression is a tail recursion
|
// Check if this expression is a tail recursion
|
||||||
if (expression instanceof FunctionCall funCall) {
|
if (expression instanceof FunctionCall funCall) {
|
||||||
if (funCall.name.equals(this.currentFunctionDefinitionName)) {
|
if (funCall.name.equals(currentFunctionDefinitionName)) {
|
||||||
// Flag this function call
|
// Flag this function call
|
||||||
funCall.isTailRecursive = true;
|
funCall.isTailRecursive = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
result.type = expression.type;
|
var result = new ReturnStatement(expression);
|
||||||
result.line = ctx.start.getLine();
|
result.line = ctx.start.getLine();
|
||||||
|
result.type = expression.type;
|
||||||
result.col = ctx.start.getCharPositionInLine();
|
result.col = ctx.start.getCharPositionInLine();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -749,6 +767,11 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
var line = ctx.start.getLine();
|
var line = ctx.start.getLine();
|
||||||
var col = ctx.start.getCharPositionInLine();
|
var col = ctx.start.getCharPositionInLine();
|
||||||
|
|
||||||
|
if (structFieldType.equals(Type.getVoidType())) {
|
||||||
|
var error = "Type void can not be used as a struct field type.";
|
||||||
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
|
}
|
||||||
|
|
||||||
if (!structFieldType.isPrimitiveType() && !structDefs.containsKey(structFieldType.getName()) && !enumDefs.containsKey(structFieldType.getName())) {
|
if (!structFieldType.isPrimitiveType() && !structDefs.containsKey(structFieldType.getName()) && !enumDefs.containsKey(structFieldType.getName())) {
|
||||||
var error = "Type " + structFieldType.getName() + " not defined.";
|
var error = "Type " + structFieldType.getName() + " not defined.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
@@ -779,7 +802,8 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
currentDeclaredReturnType = returnType;
|
currentDeclaredReturnType = returnType;
|
||||||
currentFunctionDefinitionName = name;
|
currentFunctionDefinitionName = name;
|
||||||
|
|
||||||
if (!returnType.isPrimitiveType() && !structDefs.containsKey(returnType.getName()) && !enumDefs.containsKey(returnType.getName())) {
|
var typeNotDefined = !returnType.isPrimitiveType() && !structDefs.containsKey(returnType.getName()) && !enumDefs.containsKey(returnType.getName());
|
||||||
|
if (!returnType.equals(Type.getVoidType()) && typeNotDefined) {
|
||||||
var line = ctx.returnType.start.getLine();
|
var line = ctx.returnType.start.getLine();
|
||||||
var col = ctx.returnType.start.getCharPositionInLine();
|
var col = ctx.returnType.start.getCharPositionInLine();
|
||||||
var error = "Type " + returnType.getName() + " not defined.";
|
var error = "Type " + returnType.getName() + " not defined.";
|
||||||
@@ -807,7 +831,7 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
|
|
||||||
// Visit the block, make sure that a return value is guaranteed
|
// Visit the block, make sure that a return value is guaranteed
|
||||||
var block = visit(ctx.braced_block());
|
var block = visit(ctx.braced_block());
|
||||||
if (block.type == null) {
|
if (block.type == null && !returnType.equals(Type.getVoidType())) {
|
||||||
var line = ctx.braced_block().start.getLine();
|
var line = ctx.braced_block().start.getLine();
|
||||||
var col = ctx.braced_block().start.getCharPositionInLine();
|
var col = ctx.braced_block().start.getCharPositionInLine();
|
||||||
var error = "Function " + name + " has to return something of type " + returnType.getName() + ".";
|
var error = "Function " + name + " has to return something of type " + returnType.getName() + ".";
|
||||||
@@ -826,26 +850,29 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
public Node visitParameter(KlangParser.ParameterContext ctx) {
|
public Node visitParameter(KlangParser.ParameterContext ctx) {
|
||||||
var parameterName = ctx.IDENT().getText();
|
var parameterName = ctx.IDENT().getText();
|
||||||
var parameterType = Type.getByName(ctx.type_annotation().type().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)) {
|
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.";
|
var error = "Parameter name " + parameterName + " duplicates a struct of the same name.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (enumDefs.containsKey(parameterName)) {
|
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.";
|
var error = "Parameter name " + parameterName + " duplicates an enum of the same name.";
|
||||||
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parameterType.isPrimitiveType() && !structDefs.containsKey(parameterType.getName()) && !enumDefs.containsKey(parameterType.getName())) {
|
if (!parameterType.isPrimitiveType() && !structDefs.containsKey(parameterType.getName()) && !enumDefs.containsKey(parameterType.getName())) {
|
||||||
var line = ctx.type_annotation().start.getLine();
|
var typeLine = ctx.type_annotation().start.getLine();
|
||||||
var col = ctx.type_annotation().start.getCharPositionInLine();
|
var typeCol = ctx.type_annotation().start.getCharPositionInLine();
|
||||||
var error = "Type " + parameterType.getName() + " not defined.";
|
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);
|
var parameter = new Parameter(parameterName);
|
||||||
@@ -983,4 +1010,4 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
|
|||||||
}
|
}
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package de.hsrm.compiler.Klang;
|
package de.hsrm.compiler.Klang;
|
||||||
|
|
||||||
|
import de.hsrm.compiler.Klang.helper.*;
|
||||||
import de.hsrm.compiler.Klang.nodes.EnumDefinition;
|
import de.hsrm.compiler.Klang.nodes.EnumDefinition;
|
||||||
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
|
import de.hsrm.compiler.Klang.nodes.FunctionDefinition;
|
||||||
import de.hsrm.compiler.Klang.nodes.Node;
|
import de.hsrm.compiler.Klang.nodes.Node;
|
||||||
import de.hsrm.compiler.Klang.nodes.StructDefinition;
|
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.EvalVisitor;
|
||||||
import de.hsrm.compiler.Klang.visitors.GenASM;
|
import de.hsrm.compiler.Klang.visitors.GenASM;
|
||||||
import de.hsrm.compiler.Klang.visitors.PrettyPrintVisitor;
|
import de.hsrm.compiler.Klang.visitors.PrettyPrintVisitor;
|
||||||
@@ -120,7 +122,11 @@ public class Klang {
|
|||||||
System.out.println("\nEvaluating the source code:");
|
System.out.println("\nEvaluating the source code:");
|
||||||
EvalVisitor evalVisitor = new EvalVisitor(structDefs);
|
EvalVisitor evalVisitor = new EvalVisitor(structDefs);
|
||||||
Value result = root.welcome(evalVisitor);
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ public class ReturnStatement extends Statement {
|
|||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ReturnStatement() {
|
||||||
|
this.expression = null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R> R welcome(Visitor<R> v) {
|
public <R> R welcome(Visitor<R> v) {
|
||||||
return v.visit(this);
|
return v.visit(this);
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ public class BooleanType extends PrimitiveType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Every remaining type will throw a RuntimeException
|
// Every remaining type will throw a RuntimeException
|
||||||
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
|
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class FloatType extends NumericType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Every remaining type will throw a RuntimeException
|
// Every remaining type will throw a RuntimeException
|
||||||
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
|
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ public class IntegerType extends NumericType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Every remaining type will throw a RuntimeException
|
// Every remaining type will throw a RuntimeException
|
||||||
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
|
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ public class NullType extends Type {
|
|||||||
public Type combine(Type that) {
|
public Type combine(Type that) {
|
||||||
// You can not combine null with a primitive type
|
// You can not combine null with a primitive type
|
||||||
if (that.isPrimitiveType()) {
|
if (that.isPrimitiveType()) {
|
||||||
throw new RuntimeException("Type missmatch: cannot combine " + this.getName() + " and " + that.getName());
|
throw new RuntimeException("Type mismatch: cannot combine " + this.getName() + " and " + that.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Everything else combines with null to the type it was before
|
// Everything else combines with null to the type it was before
|
||||||
|
|||||||
@@ -27,12 +27,17 @@ public abstract class Type {
|
|||||||
return NullType.getType();
|
return NullType.getType();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static VoidType getVoidType() {
|
||||||
|
return VoidType.getType();
|
||||||
|
}
|
||||||
|
|
||||||
public static Type getByName(String name) {
|
public static Type getByName(String name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "bool": return getBooleanType();
|
case "bool": return getBooleanType();
|
||||||
case "int": return getIntegerType();
|
case "int": return getIntegerType();
|
||||||
case "float": return getFloatType();
|
case "float": return getFloatType();
|
||||||
case "null": return getNullType();
|
case "null": return getNullType();
|
||||||
|
case "void": return getVoidType();
|
||||||
default: return new NamedType(name);
|
default: return new NamedType(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -42,4 +47,4 @@ public abstract class Type {
|
|||||||
public abstract boolean valuesEqual(Value a, Value b);
|
public abstract boolean valuesEqual(Value a, Value b);
|
||||||
public abstract boolean isPrimitiveType();
|
public abstract boolean isPrimitiveType();
|
||||||
public abstract boolean isNumericType();
|
public abstract boolean isNumericType();
|
||||||
}
|
}
|
||||||
|
|||||||
45
src/main/java/de/hsrm/compiler/Klang/types/VoidType.java
Normal file
45
src/main/java/de/hsrm/compiler/Klang/types/VoidType.java
Normal file
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -396,6 +396,9 @@ public class EvalVisitor implements Visitor<Value> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Value visit(ReturnStatement e) {
|
public Value visit(ReturnStatement e) {
|
||||||
|
if (e.expression == null) {
|
||||||
|
return new Value(null, Type.getVoidType());
|
||||||
|
}
|
||||||
return e.expression.welcome(this);
|
return e.expression.welcome(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -525,7 +525,9 @@ public class GenASM implements Visitor<Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(ReturnStatement e) {
|
public Void visit(ReturnStatement e) {
|
||||||
e.expression.welcome(this);
|
if (e.expression != null) {
|
||||||
|
e.expression.welcome(this);
|
||||||
|
}
|
||||||
|
|
||||||
// The ReturnStatement visitor is kindly removing the
|
// The ReturnStatement visitor is kindly removing the
|
||||||
// stack space that was allocated by the FunctionDefinition
|
// stack space that was allocated by the FunctionDefinition
|
||||||
@@ -1020,4 +1022,4 @@ public class GenASM implements Visitor<Void> {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -194,7 +194,9 @@ class GetVars implements Visitor<Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(ReturnStatement e) {
|
public Void visit(ReturnStatement e) {
|
||||||
e.expression.welcome(this);
|
if (e.expression != null) {
|
||||||
|
e.expression.welcome(this);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -310,8 +310,11 @@ public class PrettyPrintVisitor implements Visitor<Void> {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Void visit(ReturnStatement e) {
|
public Void visit(ReturnStatement e) {
|
||||||
ex.write("return ");
|
ex.write("return");
|
||||||
e.expression.welcome(this);
|
if (e.expression != null) {
|
||||||
|
ex.write(" ");
|
||||||
|
e.expression.welcome(this);
|
||||||
|
}
|
||||||
ex.write(";");
|
ex.write(";");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ public class ConstructorCallTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void numConstructorParameterMissmatch() {
|
void numConstructorParametermismatch() {
|
||||||
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(1, false); } foo();");
|
ParseTree tree = Helper.prepareParser("struct bar { a: int; } function foo(): bar { return create bar(1, false); } foo();");
|
||||||
var funcs = Helper.getFuncs(tree);
|
var funcs = Helper.getFuncs(tree);
|
||||||
var structs = Helper.getStructs(tree);
|
var structs = Helper.getStructs(tree);
|
||||||
@@ -41,6 +41,6 @@ public class ConstructorCallTest {
|
|||||||
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
|
ContextAnalysis ctxAnal = new ContextAnalysis(funcs, structs, enums);
|
||||||
|
|
||||||
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
Exception e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
||||||
assertEquals("Error in line 1:63 argument 0 Type missmatch: cannot combine bool and int", e.getMessage());
|
assertEquals("Error in line 1:63 argument 0 Type mismatch: cannot combine bool and int", e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,7 @@ public class FunctionCallTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void parameterTypeMissmatch() {
|
void parameterTypeMismatch() {
|
||||||
ParseTree tree = Helper.prepareParser("function foo(x: int): int { return x; } foo(false);");
|
ParseTree tree = Helper.prepareParser("function foo(x: int): int { return x; } foo(false);");
|
||||||
var funcs = Helper.getFuncs(tree);
|
var funcs = Helper.getFuncs(tree);
|
||||||
var structs = Helper.getStructs(tree);
|
var structs = Helper.getStructs(tree);
|
||||||
|
|||||||
101
src/test/java/VoidTest.java
Normal file
101
src/test/java/VoidTest.java
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
import de.hsrm.compiler.Klang.ContextAnalysis;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
|
|
||||||
|
public class VoidTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotThrowIfVoidIsUsedAsReturnType() {
|
||||||
|
// given
|
||||||
|
var tree = Helper.prepareParser("function foo(): void { let a: int = 0; } foo();");
|
||||||
|
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
assertDoesNotThrow(() -> ctxAnal.visit(tree));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldNotThrowIfVoidFunctionIsCalled() {
|
||||||
|
// given
|
||||||
|
var tree = Helper.prepareParser("""
|
||||||
|
function foo(): void {
|
||||||
|
let a: int = 0;
|
||||||
|
}
|
||||||
|
function bar(): int {
|
||||||
|
foo();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bar();
|
||||||
|
""");
|
||||||
|
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
assertDoesNotThrow(() -> ctxAnal.visit(tree));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowIfVoidFunctionIsAssignedToVariable() {
|
||||||
|
// given
|
||||||
|
var tree = Helper.prepareParser("""
|
||||||
|
function foo(): void {
|
||||||
|
let a: int = 0;
|
||||||
|
}
|
||||||
|
function bar(): int {
|
||||||
|
let a: int = foo();
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
bar();
|
||||||
|
""");
|
||||||
|
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
||||||
|
assertEquals("Error in line 5:4 Type mismatch: cannot combine int and void", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowIfValueIsReturnedFromVoidFunction() {
|
||||||
|
// given
|
||||||
|
var tree = Helper.prepareParser("function foo(): void { return 1; } foo();");
|
||||||
|
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
||||||
|
assertEquals("Error in line 1:23 Cannot return an expression from a void function.", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowIfVoidIsUsedAsParameterType() {
|
||||||
|
// given
|
||||||
|
var tree = Helper.prepareParser("function foo(a: void): int { return 1; } foo();");
|
||||||
|
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
||||||
|
assertEquals("Error in line 1:13 Type void cannot be used to declare a parameter.", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowIfVoidIsUsedAsVariableType() {
|
||||||
|
// given
|
||||||
|
var tree = Helper.prepareParser("function foo(): int { let a: void; return 1; } foo();");
|
||||||
|
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
||||||
|
assertEquals("Error in line 1:22 Type void can not be used to declare variables.", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldThrowIfVoidIsUsedAsStructFieldType() {
|
||||||
|
// given
|
||||||
|
var tree = Helper.prepareParser("struct bar { a: void; } function foo(): int { return 1; } foo();");
|
||||||
|
var ctxAnal = new ContextAnalysis(Helper.getFuncs(tree), Helper.getStructs(tree), Helper.getEnums(tree));
|
||||||
|
|
||||||
|
// when / then
|
||||||
|
var e = assertThrows(RuntimeException.class, () -> ctxAnal.visit(tree));
|
||||||
|
assertEquals("Error in line 1:13 Type void can not be used as a struct field type.", e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user