From 33cbae0eeb79765a0591a1aa5ac666f883a02994 Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 22:19:36 +0100 Subject: [PATCH 01/11] implement method to generate an error prefix that mentions the line and column the error occured in --- src/main/java/de/hsrm/compiler/Klang/helper/Helper.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/de/hsrm/compiler/Klang/helper/Helper.java diff --git a/src/main/java/de/hsrm/compiler/Klang/helper/Helper.java b/src/main/java/de/hsrm/compiler/Klang/helper/Helper.java new file mode 100644 index 0000000..2788a28 --- /dev/null +++ b/src/main/java/de/hsrm/compiler/Klang/helper/Helper.java @@ -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 + " "; + } +} \ No newline at end of file From 12c1f75602c15684d869415bba5ca25ad5e38dc3 Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 22:20:25 +0100 Subject: [PATCH 02/11] add fields to save the line and column in which the token corresponding to the node was found --- src/main/java/de/hsrm/compiler/Klang/nodes/Node.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/de/hsrm/compiler/Klang/nodes/Node.java b/src/main/java/de/hsrm/compiler/Klang/nodes/Node.java index b26e247..a8f033a 100644 --- a/src/main/java/de/hsrm/compiler/Klang/nodes/Node.java +++ b/src/main/java/de/hsrm/compiler/Klang/nodes/Node.java @@ -5,5 +5,8 @@ import de.hsrm.compiler.Klang.visitors.*; public abstract class Node { public Type type; + public int line; + public int col; + public abstract R welcome(Visitor v); } \ No newline at end of file From cb0d63a71c64aa6675fa2e309f5647bc2f42842a Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 22:20:59 +0100 Subject: [PATCH 03/11] save the line and column of the corresponding token, generate an error prefix for every thrown error --- .../hsrm/compiler/Klang/ContextAnalysis.java | 130 ++++++++++++++++-- 1 file changed, 115 insertions(+), 15 deletions(-) diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index d326bff..3125816 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -4,6 +4,7 @@ import java.util.Map; import java.util.HashMap; 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.expressions.*; import de.hsrm.compiler.Klang.nodes.loops.DoWhileLoop; @@ -30,6 +31,8 @@ public class ContextAnalysis extends KlangBaseVisitor { Expression expression = (Expression) this.visit(ctx.expression()); Program result = new Program(funcs, expression); result.type = expression.type; + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -81,14 +84,20 @@ public class ContextAnalysis extends KlangBaseVisitor { result.type = this.currentDeclaredReturnType; } + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @Override public Node visitPrint(KlangParser.PrintContext ctx) { + ctx.start.getLine(); + ctx.start.getCharPositionInLine(); Node expression = this.visit(ctx.expression()); PrintStatement result = new PrintStatement((Expression) expression); result.type = null; + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -115,6 +124,8 @@ public class ContextAnalysis extends KlangBaseVisitor { result.type = thenBlock.type.combine(type); } + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -122,14 +133,20 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitWhileLoop(KlangParser.WhileLoopContext ctx) { Node condition = this.visit(ctx.cond); 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 public Node visitDoWhileLoop(KlangParser.DoWhileLoopContext ctx) { Node condition = this.visit(ctx.cond); 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 @@ -138,16 +155,22 @@ public class ContextAnalysis extends KlangBaseVisitor { Node condition = this.visit(ctx.cond); Node step = this.visit(ctx.step); 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 public Node visitVariable_declaration(KlangParser.Variable_declarationContext ctx) { String name = ctx.IDENT().getText(); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); Type declaredType = Type.getByName(ctx.type_annotation().type().getText()); 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 @@ -164,16 +187,21 @@ public class ContextAnalysis extends KlangBaseVisitor { // Add it to the global map of variable declarations this.vars.put(name, result); + result.line = line; + result.col = col; return result; } @Override public Node visitVariable_assignment(KlangParser.Variable_assignmentContext ctx) { String name = ctx.IDENT().getText(); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); VariableDeclaration var = this.vars.get(name); 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 @@ -183,7 +211,10 @@ public class ContextAnalysis extends KlangBaseVisitor { expression.type.combine(var.type); // 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 @@ -191,6 +222,8 @@ public class ContextAnalysis extends KlangBaseVisitor { Expression expression = (Expression) this.visit(ctx.expression()); ReturnStatement result = new ReturnStatement(expression); result.type = expression.type; + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -200,6 +233,8 @@ public class ContextAnalysis extends KlangBaseVisitor { Node rhs = this.visit(ctx.rhs); OrExpression result = new OrExpression((Expression) lhs, (Expression) rhs); result.type = lhs.type.combine(rhs.type); + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -209,6 +244,8 @@ public class ContextAnalysis extends KlangBaseVisitor { Node rhs = this.visit(ctx.rhs); AndExpression result = new AndExpression((Expression) lhs, (Expression) rhs); result.type = lhs.type.combine(rhs.type); + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -218,6 +255,8 @@ public class ContextAnalysis extends KlangBaseVisitor { Node rhs = this.visit(ctx.rhs); AdditionExpression result = new AdditionExpression((Expression) lhs, (Expression) rhs); result.type = lhs.type.combine(rhs.type); + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -230,13 +269,18 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitEqualityExpression(KlangParser.EqualityExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); 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()) { - 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); } EqualityExpression result = new EqualityExpression((Expression) lhs, (Expression) rhs); result.type = Type.getBooleanType(); + result.line = line; + result.col = col; return result; } @@ -244,13 +288,18 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitNotEqualityExpression(KlangParser.NotEqualityExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); 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()) { - 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); result.type = Type.getBooleanType(); + result.line = line; + result.col = col; return result; } @@ -258,13 +307,18 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitLessThanExpression(KlangParser.LessThanExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); 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()) { - 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); result.type = Type.getBooleanType(); + result.line = line; + result.col = col; return result; } @@ -272,13 +326,18 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitGreaterThanExpression(KlangParser.GreaterThanExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); 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()) { - 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); result.type = Type.getBooleanType(); + result.line = line; + result.col = col; return result; } @@ -286,13 +345,18 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitLessThanOrEqualToExpression(KlangParser.LessThanOrEqualToExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); 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()) { - 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); result.type = Type.getBooleanType(); + result.line = line; + result.col = col; return result; } @@ -300,13 +364,18 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitGreaterThanOrEqualToExpression(KlangParser.GreaterThanOrEqualToExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); 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()) { - 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); result.type = Type.getBooleanType(); + result.line = line; + result.col = col; return result; } @@ -316,6 +385,8 @@ public class ContextAnalysis extends KlangBaseVisitor { Node rhs = this.visit(ctx.rhs); SubstractionExpression result = new SubstractionExpression((Expression) lhs, (Expression) rhs); result.type = lhs.type.combine(rhs.type); + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -325,6 +396,8 @@ public class ContextAnalysis extends KlangBaseVisitor { Node rhs = this.visit(ctx.rhs); MultiplicationExpression result = new MultiplicationExpression((Expression) lhs, (Expression) rhs); result.type = lhs.type.combine(rhs.type); + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -334,6 +407,8 @@ public class ContextAnalysis extends KlangBaseVisitor { Node rhs = this.visit(ctx.rhs); DivisionExpression result = new DivisionExpression((Expression) lhs, (Expression) rhs); result.type = lhs.type.combine(rhs.type); + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -343,6 +418,8 @@ public class ContextAnalysis extends KlangBaseVisitor { Node rhs = this.visit(ctx.rhs); ModuloExpression result = new ModuloExpression((Expression) lhs, (Expression) rhs); result.type = lhs.type.combine(rhs.type); + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -351,6 +428,8 @@ public class ContextAnalysis extends KlangBaseVisitor { Node expression = this.visit(ctx.expression()); NegateExpression result = new NegateExpression((Expression) expression); result.type = expression.type; + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -359,20 +438,27 @@ public class ContextAnalysis extends KlangBaseVisitor { Node expression = this.visit(ctx.expression()); NotExpression result = new NotExpression((Expression) expression); result.type = expression.type; + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @Override public Node visitVariable(KlangParser.VariableContext ctx) { String name = ctx.IDENT().getText(); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); VariableDeclaration var = this.vars.get(name); 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()); result.type = var.type; + result.line = line; + result.col = col; return result; } @@ -385,6 +471,8 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitIntAtom(KlangParser.IntAtomContext ctx) { Node n = new IntegerExpression(Integer.parseInt(ctx.getText())); n.type = Type.getIntegerType(); + n.line = ctx.start.getLine(); + n.col = ctx.start.getCharPositionInLine(); return n; } @@ -392,6 +480,8 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitBoolAtom(KlangParser.BoolAtomContext ctx) { Node n = new BooleanExpression(ctx.getText().equals("true") ? true : false); n.type = Type.getBooleanType(); + n.line = ctx.start.getLine(); + n.col = ctx.start.getCharPositionInLine(); return n; } @@ -426,6 +516,8 @@ public class ContextAnalysis extends KlangBaseVisitor { FunctionDefinition result = new FunctionDefinition(name, params, (Block) block); result.type = returnType; + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @@ -435,23 +527,29 @@ public class ContextAnalysis extends KlangBaseVisitor { Type type = Type.getByName(ctx.type_annotation().type().getText()); Parameter result = new Parameter(name); result.type = type; + result.line = ctx.start.getLine(); + result.col = ctx.start.getCharPositionInLine(); return result; } @Override public Node visitFunctionCallExpression(KlangParser.FunctionCallExpressionContext ctx) { String name = ctx.functionCall().IDENT().getText(); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); FunctionInformation func = this.funcs.get(name); 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 int argCount = ctx.functionCall().arguments().expression().size(); int paramCount = func.parameters.size(); 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 @@ -464,6 +562,8 @@ public class ContextAnalysis extends KlangBaseVisitor { FunctionCall result = new FunctionCall(name, args); result.type = func.returnType; + result.line = line; + result.col = col; return result; } } \ No newline at end of file From 3e8a904cb86f634242b935cabe7a71dded234a1e Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 22:21:30 +0100 Subject: [PATCH 04/11] catch error to abort further processing --- .../java/de/hsrm/compiler/Klang/Klang.java | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/main/java/de/hsrm/compiler/Klang/Klang.java b/src/main/java/de/hsrm/compiler/Klang/Klang.java index 6142a18..389e8bf 100644 --- a/src/main/java/de/hsrm/compiler/Klang/Klang.java +++ b/src/main/java/de/hsrm/compiler/Klang/Klang.java @@ -83,29 +83,35 @@ public class Klang { // Parse tokens to AST ParseTree tree = parser.parse(); // begin parsing at init rule - // Extract information about all functions - var functionDefinitions = new HashMap(); - new GetFunctions(functionDefinitions).visit(tree); - // Context Analysis and DAST generation - ContextAnalysis ctxAnal = new ContextAnalysis(functionDefinitions); - Node node = ctxAnal.visit(tree); // this gets us the DAST + Node root; + try { + // Extract information about all functions + var functionDefinitions = new HashMap(); + 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) { // Pretty Print the sourcecode StringWriter w = new StringWriter(); PrettyPrintVisitor.ExWriter ex = new PrettyPrintVisitor.ExWriter(w); PrettyPrintVisitor printVisitor = new PrettyPrintVisitor(ex); - node.welcome(printVisitor); - generateOutput(out, w.toString()); - return; + root.welcome(printVisitor); + System.out.println(w.toString()); } if (evaluate) { // Evaluate the sourcecode and print the result System.out.println("\nEvaluating the source code:"); EvalVisitor evalVisitor = new EvalVisitor(); - Value result = node.welcome(evalVisitor); + Value result = root.welcome(evalVisitor); if (result != null) { generateOutput(out, "Result was: TODO"); } else { @@ -119,7 +125,7 @@ public class Klang { StringWriter wAsm = new StringWriter(); GenASM.ExWriter exAsm = new GenASM.ExWriter(wAsm); GenASM genasm = new GenASM(exAsm, mainName); - node.welcome(genasm); + root.welcome(genasm); generateOutput(out, wAsm.toString()); } } From 3a89ab2231c78c0e8db7d57dd986ebdc5a82063f Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 22:24:27 +0100 Subject: [PATCH 05/11] remove error handling, since these kinds of errors are caught by our context analysis --- .../hsrm/compiler/Klang/visitors/EvalVisitor.java | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) 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 a999389..29649ee 100644 --- a/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java +++ b/src/main/java/de/hsrm/compiler/Klang/visitors/EvalVisitor.java @@ -152,13 +152,7 @@ public class EvalVisitor implements Visitor { @Override public Value visit(Variable e) { - Value result = this.env.get(e.name); - - if (result == null) { - throw new RuntimeException("Variable with name " + e.name + " not found."); - } - - return result; + return this.env.get(e.name); } @Override @@ -271,11 +265,6 @@ public class EvalVisitor implements Visitor { // Die funktionsdefinition speichern 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 Map newEnv = new HashMap<>(); for (int i = 0; i < func.parameters.length; i++) { From 02fb7b7cc2f2645367aa3314da61790a4591bcc0 Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 22:28:21 +0100 Subject: [PATCH 06/11] unify error message appearance --- .../java/de/hsrm/compiler/Klang/ContextAnalysis.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index 3125816..34713cc 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -273,7 +273,7 @@ public class ContextAnalysis extends KlangBaseVisitor { int col = ctx.start.getCharPositionInLine(); if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { - String error = "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); } @@ -292,7 +292,7 @@ public class ContextAnalysis extends KlangBaseVisitor { int col = ctx.start.getCharPositionInLine(); if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { - String error = "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); } @@ -311,7 +311,7 @@ public class ContextAnalysis extends KlangBaseVisitor { int col = ctx.start.getCharPositionInLine(); if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { - String error = "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); } @@ -330,7 +330,7 @@ public class ContextAnalysis extends KlangBaseVisitor { int col = ctx.start.getCharPositionInLine(); if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { - String error = "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); } @@ -349,7 +349,7 @@ public class ContextAnalysis extends KlangBaseVisitor { int col = ctx.start.getCharPositionInLine(); if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { - String error = "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); } @@ -368,7 +368,7 @@ public class ContextAnalysis extends KlangBaseVisitor { int col = ctx.start.getCharPositionInLine(); if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { - String error = "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); } From 7c56f401ac60756c6ac55bd4e483529cf10a9891 Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 22:28:37 +0100 Subject: [PATCH 07/11] add error prefix to the runtime exception message --- src/main/java/de/hsrm/compiler/Klang/GetFunctions.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java b/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java index 9250ad9..f9faa5c 100644 --- a/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java +++ b/src/main/java/de/hsrm/compiler/Klang/GetFunctions.java @@ -25,9 +25,12 @@ public class GetFunctions extends KlangBaseVisitor { @Override public Void visitFunctionDef(KlangParser.FunctionDefContext ctx) { String name = ctx.funcName.getText(); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); 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()); From 6f0a3754bd74ab6de956585fb41f56a3a26dfb25 Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 22:36:24 +0100 Subject: [PATCH 08/11] check whether the body of a function guarantees a return value and throw an exception if not --- .../java/de/hsrm/compiler/Klang/ContextAnalysis.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index 34713cc..518439f 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -488,6 +488,8 @@ public class ContextAnalysis extends KlangBaseVisitor { @Override public Node visitFunctionDef(KlangParser.FunctionDefContext ctx) { String name = ctx.funcName.getText(); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); Type returnType = Type.getByName(ctx.returnType.type().getText()); this.currentDeclaredReturnType = returnType; @@ -509,9 +511,12 @@ public class ContextAnalysis extends KlangBaseVisitor { 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()); - 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); result.type = returnType; From 018ce8712a8f1eba317032adebf522384e94644e Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 22:56:40 +0100 Subject: [PATCH 09/11] add error message prefix to type missmatch errors, staticly assign bool type to boolean expressions --- .../hsrm/compiler/Klang/ContextAnalysis.java | 88 ++++++++++++++----- 1 file changed, 68 insertions(+), 20 deletions(-) diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index 518439f..19b9625 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -59,7 +59,11 @@ public class ContextAnalysis extends KlangBaseVisitor { // for which the VariableDeclaration is an exception if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) { // 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 hasReturn = true; @@ -121,7 +125,9 @@ public class ContextAnalysis extends KlangBaseVisitor { } 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(); @@ -177,7 +183,11 @@ public class ContextAnalysis extends KlangBaseVisitor { VariableDeclaration result; if (ctx.expression() != null) { 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.type = declaredType; // add the type only if there is an expression } else { @@ -208,7 +218,11 @@ public class ContextAnalysis extends KlangBaseVisitor { Expression expression = (Expression) this.visit(ctx.expression()); // 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 Node result = new VariableAssignment(name, expression); @@ -232,7 +246,7 @@ public class ContextAnalysis extends KlangBaseVisitor { Node lhs = this.visit(ctx.lhs); Node rhs = this.visit(ctx.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; @@ -243,7 +257,7 @@ public class ContextAnalysis extends KlangBaseVisitor { Node lhs = this.visit(ctx.lhs); Node rhs = this.visit(ctx.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; @@ -253,8 +267,14 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitAdditionExpression(KlangParser.AdditionExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); Node rhs = this.visit(ctx.rhs); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); 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; @@ -383,10 +403,16 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitSubstractionExpression(KlangParser.SubstractionExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); Node rhs = this.visit(ctx.rhs); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); SubstractionExpression result = new SubstractionExpression((Expression) lhs, (Expression) rhs); - result.type = lhs.type.combine(rhs.type); - result.line = ctx.start.getLine(); - result.col = ctx.start.getCharPositionInLine(); + 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; } @@ -394,10 +420,16 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitMultiplicationExpression(KlangParser.MultiplicationExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); Node rhs = this.visit(ctx.rhs); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); MultiplicationExpression result = new MultiplicationExpression((Expression) lhs, (Expression) rhs); - result.type = lhs.type.combine(rhs.type); - result.line = ctx.start.getLine(); - result.col = ctx.start.getCharPositionInLine(); + 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; } @@ -405,10 +437,16 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitDivisionExpression(KlangParser.DivisionExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); Node rhs = this.visit(ctx.rhs); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); DivisionExpression result = new DivisionExpression((Expression) lhs, (Expression) rhs); - result.type = lhs.type.combine(rhs.type); - result.line = ctx.start.getLine(); - result.col = ctx.start.getCharPositionInLine(); + 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; } @@ -416,10 +454,16 @@ public class ContextAnalysis extends KlangBaseVisitor { public Node visitModuloExpression(KlangParser.ModuloExpressionContext ctx) { Node lhs = this.visit(ctx.lhs); Node rhs = this.visit(ctx.rhs); + int line = ctx.start.getLine(); + int col = ctx.start.getCharPositionInLine(); ModuloExpression result = new ModuloExpression((Expression) lhs, (Expression) rhs); - result.type = lhs.type.combine(rhs.type); - result.line = ctx.start.getLine(); - result.col = ctx.start.getCharPositionInLine(); + 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; } @@ -561,7 +605,11 @@ public class ContextAnalysis extends KlangBaseVisitor { Expression[] args = new Expression[argCount]; for (int i = 0; i < argCount; 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; } From e8f80eb2f95fcc433eb7afa31cc7e428db90fbb1 Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 22:59:17 +0100 Subject: [PATCH 10/11] make file pretty --- .../hsrm/compiler/Klang/ContextAnalysis.java | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index 19b9625..9b20bf9 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -29,7 +29,7 @@ public class ContextAnalysis extends KlangBaseVisitor { funcs[i] = (FunctionDefinition) this.visit(ctx.functionDef(i)); } Expression expression = (Expression) this.visit(ctx.expression()); - Program result = new Program(funcs, expression); + Program result = new Program(funcs, expression); result.type = expression.type; result.line = ctx.start.getLine(); result.col = ctx.start.getCharPositionInLine(); @@ -55,17 +55,19 @@ public class ContextAnalysis extends KlangBaseVisitor { statements[i] = (Statement) currentStatement; actualStatementCount += 1; - // We use the existance of a type to indicate that this statement returns something - // for which the VariableDeclaration is an exception + // We use the existance of a type to indicate that this statement returns + // something for which the VariableDeclaration is an exception if (currentStatement.type != null && !(currentStatement instanceof VariableDeclaration)) { // check whether the type matches try { this.currentDeclaredReturnType.combine(currentStatement.type); } catch (Exception e) { - throw new RuntimeException(Helper.getErrorPrefix(currentStatement.line, currentStatement.col) + e.getMessage()); + 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; break; } @@ -98,7 +100,7 @@ public class ContextAnalysis extends KlangBaseVisitor { ctx.start.getLine(); ctx.start.getCharPositionInLine(); Node expression = this.visit(ctx.expression()); - PrintStatement result = new PrintStatement((Expression) expression); + PrintStatement result = new PrintStatement((Expression) expression); result.type = null; result.line = ctx.start.getLine(); result.col = ctx.start.getCharPositionInLine(); @@ -125,8 +127,9 @@ public class ContextAnalysis extends KlangBaseVisitor { } if (thenBlock.type != null && type != null) { - // 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 + // 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; } @@ -176,7 +179,7 @@ public class ContextAnalysis extends KlangBaseVisitor { if (this.vars.get(name) != null) { String error = "Redeclaration of variable with name \"" + name + "\"."; - throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } // Create the appropriate instance @@ -558,7 +561,7 @@ public class ContextAnalysis extends KlangBaseVisitor { // Visit the block, make sure that a return value is guaranteed Node block = this.visit(ctx.braced_block()); if (block.type == null) { - String error = "Function " + name +" has to return something of type " + returnType.getName() +"."; + String error = "Function " + name + " has to return something of type " + returnType.getName() + "."; throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); } @@ -604,11 +607,11 @@ public class ContextAnalysis extends KlangBaseVisitor { // Evaluate every argument Expression[] args = new Expression[argCount]; 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)); 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()); + throw new RuntimeException(Helper.getErrorPrefix(line, col) + "argument " + i + " " + e.getMessage()); } args[i] = expression; } From f3c5bac8606fde99dc409214f596ec1b352c51e3 Mon Sep 17 00:00:00 2001 From: nitrix Date: Mon, 3 Feb 2020 23:45:52 +0100 Subject: [PATCH 11/11] check whether the types of the operants of an equality expression can combine instead of enforcing them to both be integers --- src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java index 9b20bf9..5091dc1 100644 --- a/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java +++ b/src/main/java/de/hsrm/compiler/Klang/ContextAnalysis.java @@ -295,9 +295,10 @@ public class ContextAnalysis extends KlangBaseVisitor { int line = ctx.start.getLine(); int col = ctx.start.getCharPositionInLine(); - if (lhs.type != Type.getIntegerType() || rhs.type != Type.getIntegerType()) { - String error = "Both operants of this expression have to be a number."; - throw new RuntimeException(Helper.getErrorPrefix(line, col) + error); + try { + 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);