Merge branch 'feature/better-errors' into 'master'

Feature/better errors

See merge request mkais001/klang!10
This commit is contained in:
Dennis Kaiser
2020-02-03 23:50:49 +01:00
6 changed files with 225 additions and 60 deletions

View File

@@ -4,6 +4,7 @@ import java.util.Map;
import java.util.HashMap; import java.util.HashMap;
import de.hsrm.compiler.Klang.helper.FunctionInformation; import de.hsrm.compiler.Klang.helper.FunctionInformation;
import de.hsrm.compiler.Klang.helper.Helper;
import de.hsrm.compiler.Klang.nodes.*; import de.hsrm.compiler.Klang.nodes.*;
import de.hsrm.compiler.Klang.nodes.expressions.*; import de.hsrm.compiler.Klang.nodes.expressions.*;
import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop; import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop;
@@ -30,6 +31,8 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Expression expression = (Expression) this.visit(ctx.expression()); Expression expression = (Expression) this.visit(ctx.expression());
Program result = new Program(funcs, expression); Program result = new Program(funcs, expression);
result.type = expression.type; result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@@ -52,13 +55,19 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
statements[i] = (Statement) currentStatement; statements[i] = (Statement) currentStatement;
actualStatementCount += 1; actualStatementCount += 1;
// We use the existance of a type to indicate that this statement returns something // We use the existance of a type to indicate that this statement returns
// for which the VariableDeclaration is an exception // something for which the VariableDeclaration is an exception
if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) { if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) {
// check whether the type matches // check whether the type matches
this.currentDeclaredReturnType.combine(currentStatement.type); try {
this.currentDeclaredReturnType.combine(currentStatement.type);
} catch (Exception e) {
throw new RuntimeException(
Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage());
}
// since we have a return guaranteed, every statement after this one is unreachable code // since we have a return guaranteed, every statement
// after this one is unreachable code
hasReturn = true; hasReturn = true;
break; break;
} }
@@ -81,14 +90,20 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
result.type = this.currentDeclaredReturnType; result.type = this.currentDeclaredReturnType;
} }
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@Override @Override
public Node visitPrint(KlangParser.PrintContext ctx) { public Node visitPrint(KlangParser.PrintContext ctx) {
ctx.start.getLine();
ctx.start.getCharPositionInLine();
Node expression = this.visit(ctx.expression()); Node expression = this.visit(ctx.expression());
PrintStatement result = new PrintStatement((Expression) expression); PrintStatement result = new PrintStatement((Expression) expression);
result.type = null; result.type = null;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@@ -112,9 +127,14 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
} }
if (thenBlock.type != null && type != null) { if (thenBlock.type != null && type != null) {
result.type = thenBlock.type.combine(type); // Since a block verifies that it can combine with the return type of the
// function it is defined in, we do not have check whether the then and
// alt block return types match
result.type = thenBlock.type;
} }
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@@ -122,14 +142,20 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitWhileLoop(KlangParser.WhileLoopContext ctx) { public Node visitWhileLoop(KlangParser.WhileLoopContext ctx) {
Node condition = this.visit(ctx.cond); Node condition = this.visit(ctx.cond);
Node block = this.visit(ctx.braced_block()); Node block = this.visit(ctx.braced_block());
return new WhileLoop((Expression) condition, (Block) block); Node result = new WhileLoop((Expression) condition, (Block) block);
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
} }
@Override @Override
public Node visitDoWhileLoop(KlangParser.DoWhileLoopContext ctx) { public Node visitDoWhileLoop(KlangParser.DoWhileLoopContext ctx) {
Node condition = this.visit(ctx.cond); Node condition = this.visit(ctx.cond);
Node block = this.visit(ctx.braced_block()); Node block = this.visit(ctx.braced_block());
return new DoWhileLoop((Expression) condition, (Block) block); Node result = new DoWhileLoop((Expression) condition, (Block) block);
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
} }
@Override @Override
@@ -138,23 +164,33 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Node condition = this.visit(ctx.cond); Node condition = this.visit(ctx.cond);
Node step = this.visit(ctx.step); Node step = this.visit(ctx.step);
Node block = this.visit(ctx.braced_block()); Node block = this.visit(ctx.braced_block());
return new ForLoop((Statement) init, (Expression) condition, (VariableAssignment) step, (Block) block); Node result = new ForLoop((Statement) init, (Expression) condition, (VariableAssignment) step, (Block) block);
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result;
} }
@Override @Override
public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) { public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) {
String name = ctx.IDENT().getText(); String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type declaredType = Type.getByName(ctx.type_annotation().type().getText()); Type declaredType = Type.getByName(ctx.type_annotation().type().getText());
if (this.vars.get(name) != null) { if (this.vars.get(name) != null) {
throw new RuntimeException("Redeclaration of variable with name \"" + name + "\"."); String error = "Redeclaration of variable with name \"" + name + "\".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
// Create the appropriate instance // Create the appropriate instance
VariableDeclaration result; VariableDeclaration result;
if (ctx.expression() != null) { if (ctx.expression() != null) {
Node expression = this.visit(ctx.expression()); Node expression = this.visit(ctx.expression());
declaredType = declaredType.combine(expression.type); try {
declaredType = declaredType.combine(expression.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
result = new VariableDeclaration(name, (Expression) expression); result = new VariableDeclaration(name, (Expression) expression);
result.type = declaredType; // add the type only if there is an expression result.type = declaredType; // add the type only if there is an expression
} else { } else {
@@ -164,26 +200,38 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
// Add it to the global map of variable declarations // Add it to the global map of variable declarations
this.vars.put(name, result); this.vars.put(name, result);
result.line = line;
result.col = col;
return result; return result;
} }
@Override @Override
public Node visitVariable_assignment(KlangParser.Variable_assignmentContext ctx) { public Node visitVariable_assignment(KlangParser.Variable_assignmentContext ctx) {
String name = ctx.IDENT().getText(); String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
VariableDeclaration var = this.vars.get(name); VariableDeclaration var = this.vars.get(name);
if (var == null) { if (var == null) {
throw new RuntimeException("Variable with name \"" + name + "\" not defined."); String error = "Variable with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
// Evaluate the expression // Evaluate the expression
Expression expression = (Expression) this.visit(ctx.expression()); Expression expression = (Expression) this.visit(ctx.expression());
// Make sure expression can be assigned to the variable // Make sure expression can be assigned to the variable
expression.type.combine(var.type); try {
expression.type.combine(var.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
// Create a new node and add the type of the expression to it // Create a new node and add the type of the expression to it
return new VariableAssignment(name, expression); Node result = new VariableAssignment(name, expression);
result.line = line;
result.col = col;
return result;
} }
@Override @Override
@@ -191,6 +239,8 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Expression expression = (Expression) this.visit(ctx.expression()); Expression expression = (Expression) this.visit(ctx.expression());
ReturnStatement result = new ReturnStatement(expression); ReturnStatement result = new ReturnStatement(expression);
result.type = expression.type; result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@@ -199,7 +249,9 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
OrExpression result = new OrExpression((Expression) lhs, (Expression) rhs); OrExpression result = new OrExpression((Expression) lhs, (Expression) rhs);
result.type = lhs.type.combine(rhs.type); result.type = Type.getBooleanType();
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@@ -208,7 +260,9 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
AndExpression result = new AndExpression((Expression) lhs, (Expression) rhs); AndExpression result = new AndExpression((Expression) lhs, (Expression) rhs);
result.type = lhs.type.combine(rhs.type); result.type = Type.getBooleanType();
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@@ -216,8 +270,16 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitAdditionExpression(KlangParser.AdditionExpressionContext ctx) { public Node visitAdditionExpression(KlangParser.AdditionExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
AdditionExpression result = new AdditionExpression((Expression) lhs, (Expression) rhs); AdditionExpression result = new AdditionExpression((Expression) lhs, (Expression) rhs);
result.type = lhs.type.combine(rhs.type); try {
result.type = lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@@ -230,13 +292,19 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitEqualityExpression(KlangParser.EqualityExpressionContext ctx) { public Node visitEqualityExpression(KlangParser.EqualityExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { try {
throw new RuntimeException("Both operants of this expression have to be a number"); lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
} }
EqualityExpression result = new EqualityExpression((Expression) lhs, (Expression) rhs); EqualityExpression result = new EqualityExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result; return result;
} }
@@ -244,13 +312,18 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitNotEqualityExpression(KlangParser.NotEqualityExpressionContext ctx) { public Node visitNotEqualityExpression(KlangParser.NotEqualityExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
throw new RuntimeException("Both operants of this expression have to be a number"); String error = "Both operants of this expression have to be a number.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
NotEqualityExpression result = new NotEqualityExpression((Expression) lhs, (Expression) rhs); NotEqualityExpression result = new NotEqualityExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result; return result;
} }
@@ -258,13 +331,18 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitLessThanExpression(KlangParser.LessThanExpressionContext ctx) { public Node visitLessThanExpression(KlangParser.LessThanExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
throw new RuntimeException("Both operants of this expression have to be a number"); String error = "Both operants of this expression have to be a number.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
LTExpression result = new LTExpression((Expression) lhs, (Expression) rhs); LTExpression result = new LTExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result; return result;
} }
@@ -272,13 +350,18 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitGreaterThanExpression(KlangParser.GreaterThanExpressionContext ctx) { public Node visitGreaterThanExpression(KlangParser.GreaterThanExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
throw new RuntimeException("Both operants of this expression have to be a number"); String error = "Both operants of this expression have to be a number.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
GTExpression result = new GTExpression((Expression) lhs, (Expression) rhs); GTExpression result = new GTExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result; return result;
} }
@@ -286,13 +369,18 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitLessThanOrEqualToExpression(KlangParser.LessThanOrEqualToExpressionContext ctx) { public Node visitLessThanOrEqualToExpression(KlangParser.LessThanOrEqualToExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
throw new RuntimeException("Both operants of this expression have to be a number"); String error = "Both operants of this expression have to be a number.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
LTEExpression result = new LTEExpression((Expression) lhs, (Expression) rhs); LTEExpression result = new LTEExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result; return result;
} }
@@ -300,13 +388,18 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitGreaterThanOrEqualToExpression(KlangParser.GreaterThanOrEqualToExpressionContext ctx) { public Node visitGreaterThanOrEqualToExpression(KlangParser.GreaterThanOrEqualToExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) {
throw new RuntimeException("Both operants of this expression have to be a number"); String error = "Both operants of this expression have to be a number.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
GTEExpression result = new GTEExpression((Expression) lhs, (Expression) rhs); GTEExpression result = new GTEExpression((Expression) lhs, (Expression) rhs);
result.type = Type.getBooleanType(); result.type = Type.getBooleanType();
result.line = line;
result.col = col;
return result; return result;
} }
@@ -314,8 +407,16 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitSubstractionExpression(KlangParser.SubstractionExpressionContext ctx) { public Node visitSubstractionExpression(KlangParser.SubstractionExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
SubstractionExpression result = new SubstractionExpression((Expression) lhs, (Expression) rhs); SubstractionExpression result = new SubstractionExpression((Expression) lhs, (Expression) rhs);
result.type = lhs.type.combine(rhs.type); try {
result.type = lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
result.line = line;
result.col = col;
return result; return result;
} }
@@ -323,8 +424,16 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitMultiplicationExpression(KlangParser.MultiplicationExpressionContext ctx) { public Node visitMultiplicationExpression(KlangParser.MultiplicationExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
MultiplicationExpression result = new MultiplicationExpression((Expression) lhs, (Expression) rhs); MultiplicationExpression result = new MultiplicationExpression((Expression) lhs, (Expression) rhs);
result.type = lhs.type.combine(rhs.type); try {
result.type = lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
result.line = line;
result.col = col;
return result; return result;
} }
@@ -332,8 +441,16 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitDivisionExpression(KlangParser.DivisionExpressionContext ctx) { public Node visitDivisionExpression(KlangParser.DivisionExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
DivisionExpression result = new DivisionExpression((Expression) lhs, (Expression) rhs); DivisionExpression result = new DivisionExpression((Expression) lhs, (Expression) rhs);
result.type = lhs.type.combine(rhs.type); try {
result.type = lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
result.line = line;
result.col = col;
return result; return result;
} }
@@ -341,8 +458,16 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitModuloExpression(KlangParser.ModuloExpressionContext ctx) { public Node visitModuloExpression(KlangParser.ModuloExpressionContext ctx) {
Node lhs = this.visit(ctx.lhs); Node lhs = this.visit(ctx.lhs);
Node rhs = this.visit(ctx.rhs); Node rhs = this.visit(ctx.rhs);
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
ModuloExpression result = new ModuloExpression((Expression) lhs, (Expression) rhs); ModuloExpression result = new ModuloExpression((Expression) lhs, (Expression) rhs);
result.type = lhs.type.combine(rhs.type); try {
result.type = lhs.type.combine(rhs.type);
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + e.getMessage());
}
result.line = line;
result.col = col;
return result; return result;
} }
@@ -351,6 +476,8 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Node expression = this.visit(ctx.expression()); Node expression = this.visit(ctx.expression());
NegateExpression result = new NegateExpression((Expression) expression); NegateExpression result = new NegateExpression((Expression) expression);
result.type = expression.type; result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@@ -359,20 +486,27 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Node expression = this.visit(ctx.expression()); Node expression = this.visit(ctx.expression());
NotExpression result = new NotExpression((Expression) expression); NotExpression result = new NotExpression((Expression) expression);
result.type = expression.type; result.type = expression.type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@Override @Override
public Node visitVariable(KlangParser.VariableContext ctx) { public Node visitVariable(KlangParser.VariableContext ctx) {
String name = ctx.IDENT().getText(); String name = ctx.IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
VariableDeclaration var = this.vars.get(name); VariableDeclaration var = this.vars.get(name);
if (var == null) { if (var == null) {
throw new RuntimeException("Variable with name \"" + name + "\" not defined."); String error = "Variable with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
Variable result = new Variable(ctx.IDENT().getText()); Variable result = new Variable(ctx.IDENT().getText());
result.type = var.type; result.type = var.type;
result.line = line;
result.col = col;
return result; return result;
} }
@@ -385,6 +519,8 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitIntAtom(KlangParser.IntAtomContext ctx) { public Node visitIntAtom(KlangParser.IntAtomContext ctx) {
Node n = new IntegerExpression(Integer.parseInt(ctx.getText())); Node n = new IntegerExpression(Integer.parseInt(ctx.getText()));
n.type = Type.getIntegerType(); n.type = Type.getIntegerType();
n.line = ctx.start.getLine();
n.col = ctx.start.getCharPositionInLine();
return n; return n;
} }
@@ -392,12 +528,16 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
public Node visitBoolAtom(KlangParser.BoolAtomContext ctx) { public Node visitBoolAtom(KlangParser.BoolAtomContext ctx) {
Node n = new BooleanExpression(ctx.getText().equals("true") ? true : false); Node n = new BooleanExpression(ctx.getText().equals("true") ? true : false);
n.type = Type.getBooleanType(); n.type = Type.getBooleanType();
n.line = ctx.start.getLine();
n.col = ctx.start.getCharPositionInLine();
return n; return n;
} }
@Override @Override
public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) { public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) {
String name = ctx.funcName.getText(); String name = ctx.funcName.getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
Type returnType = Type.getByName(ctx.returnType.type().getText()); Type returnType = Type.getByName(ctx.returnType.type().getText());
this.currentDeclaredReturnType = returnType; this.currentDeclaredReturnType = returnType;
@@ -419,13 +559,18 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
this.vars.put(param.name, var); this.vars.put(param.name, var);
} }
// Visit the block, make sure the types are matching // Visit the block, make sure that a return value is guaranteed
Node block = this.visit(ctx.braced_block()); Node block = this.visit(ctx.braced_block());
block.type.combine(returnType); if (block.type == null) {
String error = "Function " + name + " has to return something of type " + returnType.getName() + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
}
FunctionDefinition result = new FunctionDefinition(name, params, (Block) block); FunctionDefinition result = new FunctionDefinition(name, params, (Block) block);
result.type = returnType; result.type = returnType;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@@ -435,35 +580,47 @@ public class ContextAnalysis extends KlangBaseVisitor<Node> {
Type type = Type.getByName(ctx.type_annotation().type().getText()); Type type = Type.getByName(ctx.type_annotation().type().getText());
Parameter result = new Parameter(name); Parameter result = new Parameter(name);
result.type = type; result.type = type;
result.line = ctx.start.getLine();
result.col = ctx.start.getCharPositionInLine();
return result; return result;
} }
@Override @Override
public Node visitFunctionCallExpression(KlangParser.FunctionCallExpressionContext ctx) { public Node visitFunctionCallExpression(KlangParser.FunctionCallExpressionContext ctx) {
String name = ctx.functionCall().IDENT().getText(); String name = ctx.functionCall().IDENT().getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
FunctionInformation func = this.funcs.get(name); FunctionInformation func = this.funcs.get(name);
if (func == null) { if (func == null) {
throw new RuntimeException("Function with name \"" + name + "\" not defined."); String error = "Function with name \"" + name + "\" not defined.";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
// Make sure the number of arguments matches the number of parameters // Make sure the number of arguments matches the number of parameters
int argCount = ctx.functionCall().arguments().expression().size(); int argCount = ctx.functionCall().arguments().expression().size();
int paramCount = func.parameters.size(); int paramCount = func.parameters.size();
if (argCount != paramCount) { if (argCount != paramCount) {
throw new RuntimeException("Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + "."); String error = "Function \"" + name + "\" expects " + paramCount + " parameters, but got " + argCount + ".";
throw new RuntimeException(Helper.getErrorPrefix(line, col) + error);
} }
// Evaluate every argument // Evaluate every argument
Expression[] args = new Expression[argCount]; Expression[] args = new Expression[argCount];
for (int i = 0; i < argCount; i++) { for (int i = 0; i < argCount; i++) {
Expression expression = (Expression) this.visit(ctx.functionCall().arguments().expression(i)); Expression expression = (Expression) this.visit(ctx.functionCall().arguments().expression(i));
expression.type.combine(func.signature[i]); // Make sure the types are matching try {
expression.type.combine(func.signature[i]); // Make sure the types are matching
} catch (Exception e) {
throw new RuntimeException(Helper.getErrorPrefix(line, col) + "argument " + i + " " + e.getMessage());
}
args[i] = expression; args[i] = expression;
} }
FunctionCall result = new FunctionCall(name, args); FunctionCall result = new FunctionCall(name, args);
result.type = func.returnType; result.type = func.returnType;
result.line = line;
result.col = col;
return result; return result;
} }
} }

View File

@@ -25,9 +25,12 @@ public class GetFunctions extends KlangBaseVisitor<Void> {
@Override @Override
public Void visitFunctionDef(KlangParser.FunctionDefContext ctx) { public Void visitFunctionDef(KlangParser.FunctionDefContext ctx) {
String name = ctx.funcName.getText(); String name = ctx.funcName.getText();
int line = ctx.start.getLine();
int col = ctx.start.getCharPositionInLine();
if (this.funcs.containsKey(name)) { if (this.funcs.containsKey(name)) {
throw new Error("Function " + name + " defined multiple times"); String error = "Function " + name + " defined multiple times.";
throw new Error(Helper.getErrorPrefix(line, col) + error);
} }
Type returnType = Type.getByName(ctx.returnType.type().getText()); Type returnType = Type.getByName(ctx.returnType.type().getText());

View File

@@ -83,29 +83,35 @@ public class Klang {
// Parse tokens to AST // Parse tokens to AST
ParseTree tree = parser.parse(); // begin parsing at init rule ParseTree tree = parser.parse(); // begin parsing at init rule
// Extract information about all functions
var functionDefinitions = new HashMap<String, FunctionInformation>();
new GetFunctions(functionDefinitions).visit(tree);
// Context Analysis and DAST generation // Context Analysis and DAST generation
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions); Node root;
Node node = ctxAnal.visit(tree); // this gets us the DAST try {
// Extract information about all functions
var functionDefinitions = new HashMap<String, FunctionInformation>();
new GetFunctions(functionDefinitions).visit(tree);
// Create the DAST
ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions);
root = ctxAnal.visit(tree);
} catch (Exception e) {
System.err.println(e.getMessage());
return;
}
if (prettyPrint) { if (prettyPrint) {
// Pretty Print the sourcecode // Pretty Print the sourcecode
StringWriter w = new StringWriter(); StringWriter w = new StringWriter();
PrettyPrintVisitor.ExWriter ex = new PrettyPrintVisitor.ExWriter(w); PrettyPrintVisitor.ExWriter ex = new PrettyPrintVisitor.ExWriter(w);
PrettyPrintVisitor printVisitor = new PrettyPrintVisitor(ex); PrettyPrintVisitor printVisitor = new PrettyPrintVisitor(ex);
node.welcome(printVisitor); root.welcome(printVisitor);
generateOutput(out, w.toString()); System.out.println(w.toString());
return;
} }
if (evaluate) { if (evaluate) {
// Evaluate the sourcecode and print the result // Evaluate the sourcecode and print the result
System.out.println("\nEvaluating the source code:"); System.out.println("\nEvaluating the source code:");
EvalVisitor evalVisitor = new EvalVisitor(); EvalVisitor evalVisitor = new EvalVisitor();
Value result = node.welcome(evalVisitor); Value result = root.welcome(evalVisitor);
if (result != null) { if (result != null) {
generateOutput(out, "Result was: TODO"); generateOutput(out, "Result was: TODO");
} else { } else {
@@ -119,7 +125,7 @@ public class Klang {
StringWriter wAsm = new StringWriter(); StringWriter wAsm = new StringWriter();
GenASM.ExWriter exAsm = new GenASM.ExWriter(wAsm); GenASM.ExWriter exAsm = new GenASM.ExWriter(wAsm);
GenASM genasm = new GenASM(exAsm, mainName); GenASM genasm = new GenASM(exAsm, mainName);
node.welcome(genasm); root.welcome(genasm);
generateOutput(out, wAsm.toString()); generateOutput(out, wAsm.toString());
} }
} }

View File

@@ -0,0 +1,7 @@
package de.hsrm.compiler.Klang.helper;
public class Helper {
public static String getErrorPrefix(int line, int col) {
return "Error in line " + line + ":" + col + " ";
}
}

View File

@@ -5,5 +5,8 @@ import de.hsrm.compiler.Klang.visitors.*;
public abstract class Node { public abstract class Node {
public Type type; public Type type;
public int line;
public int col;
public abstract <R> R welcome(Visitor<R> v); public abstract <R> R welcome(Visitor<R> v);
} }

View File

@@ -152,13 +152,7 @@ public class EvalVisitor implements Visitor<Value> {
@Override @Override
public Value visit(Variable e) { public Value visit(Variable e) {
Value result = this.env.get(e.name); return this.env.get(e.name);
if (result == null) {
throw new RuntimeException("Variable with name " + e.name + " not found.");
}
return result;
} }
@Override @Override
@@ -271,11 +265,6 @@ public class EvalVisitor implements Visitor<Value> {
// Die funktionsdefinition speichern // Die funktionsdefinition speichern
FunctionDefinition func = this.funcs.get(e.name); FunctionDefinition func = this.funcs.get(e.name);
// Stelle sicher, dass die Länge der argumente und parameter übereinstimmen
if (e.arguments.length != func.parameters.length) {
throw new RuntimeException("Error with function call " + e.name + ": Number of parameters wrong");
}
// Baue ein neues environment // Baue ein neues environment
Map<String, Value> newEnv = new HashMap<>(); Map<String, Value> newEnv = new HashMap<>();
for (int i = 0; i < func.parameters.length; i++) { for (int i = 0; i < func.parameters.length; i++) {