diff --git a/src/main/java/de/maishai/ASTGenerator.java b/src/main/java/de/maishai/ASTGenerator.java index 8cb6a11..576e360 100644 --- a/src/main/java/de/maishai/ASTGenerator.java +++ b/src/main/java/de/maishai/ASTGenerator.java @@ -14,8 +14,11 @@ import java.util.List; public class ASTGenerator { + public static CollectingErrorListener errorListener; + + public static Class generateAST(DecafParser.ClassContext ctx, CollectingErrorListener errorListener2) { + errorListener = errorListener2; - public static Class generateAST(DecafParser.ClassContext ctx) { List declarations = new ArrayList<>(); if (ctx.field() != null) { declarations = ctx.field().stream().map(ASTGenerator::generateFieldVariable).toList(); @@ -30,9 +33,9 @@ public class ASTGenerator { Block mainmeth = null; if (!ctx.mainmeth().isEmpty()) { if (ctx.mainmeth().size() > 1) { - throw new RuntimeException("Only one main method allowed!"); + errorListener.generatorError("Only one main method allowed!"); } - mainmeth = new BlockGenerator().visit(ctx.mainmeth().get(0).block()); + mainmeth = new BlockGenerator(errorListener).visit(ctx.mainmeth().get(0).block()); } List meths = new ArrayList<>(); if (ctx.meth() != null) { @@ -55,7 +58,7 @@ public class ASTGenerator { if (ctx.params() != null) { params = ctx.params().param().stream().map(ASTGenerator::generateParameter).toList(); } - Block block = new BlockGenerator().visit(ctx.block()); + Block block = new BlockGenerator(errorListener).visit(ctx.block()); return new Method(getType(ctx.returntype()), ctx.id().IDENTIFIER().getText(), params, block); } @@ -64,7 +67,7 @@ public class ASTGenerator { if (ctx.params() != null) { params = ctx.params().param().stream().map(ASTGenerator::generateParameter).toList(); } - Block block = new BlockGenerator().visit(ctx.block()); + Block block = new BlockGenerator(errorListener).visit(ctx.block()); return new Constructor( ctx.id().getText(), params, block); } @@ -78,7 +81,8 @@ public class ASTGenerator { if (ctx.id() != null) { return Type.REFERENCE(ctx.id().getText()); } - throw new RuntimeException("No type found!"); + errorListener.generatorError("No type found"); + return null; } public static Type getType(DecafParser.ReturntypeContext ctx) { if (ctx.type() != null){ @@ -87,6 +91,7 @@ public class ASTGenerator { if (ctx.VOID() != null){ return Type.VOID; } - throw new RuntimeException("No type found!"); + errorListener.generatorError("No type found"); + return null; } } diff --git a/src/main/java/de/maishai/BlockGenerator.java b/src/main/java/de/maishai/BlockGenerator.java index c13a0ae..8584e68 100644 --- a/src/main/java/de/maishai/BlockGenerator.java +++ b/src/main/java/de/maishai/BlockGenerator.java @@ -10,17 +10,22 @@ import java.util.ArrayList; import java.util.List; public class BlockGenerator extends DecafBaseVisitor { + private final CollectingErrorListener errorListener; + + public BlockGenerator(CollectingErrorListener errorListener) { + this.errorListener = errorListener; + } @Override public Block visitBlock(DecafParser.BlockContext ctx) { List statements = new ArrayList<>(ctx.stmt().size()); for (DecafParser.StmtContext stmtContext : ctx.stmt()) { - List statementList = new StatementGenerator().visit(stmtContext); + List statementList = new StatementGenerator(errorListener).visit(stmtContext); if (statementList != null){ statements.addAll(statementList); } } if (ctx.return_() != null){ - statements.add(ctx.return_().expr() == null ? new Return(null) : new Return(new ExpressionGenerator().visit(ctx.return_().expr()))); + statements.add(ctx.return_().expr() == null ? new Return(null) : new Return(new ExpressionGenerator(errorListener).visit(ctx.return_().expr()))); } return new Block(statements); } diff --git a/src/main/java/de/maishai/CollectingErrorListener.java b/src/main/java/de/maishai/CollectingErrorListener.java new file mode 100644 index 0000000..fd7972f --- /dev/null +++ b/src/main/java/de/maishai/CollectingErrorListener.java @@ -0,0 +1,26 @@ +package de.maishai; + +import org.antlr.v4.runtime.BaseErrorListener; +import org.antlr.v4.runtime.RecognitionException; +import org.antlr.v4.runtime.Recognizer; + +import java.util.ArrayList; +import java.util.List; + +public class CollectingErrorListener extends BaseErrorListener { + + private final List errors = new ArrayList<>(); + + @Override + public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) { + errors.add("Error in line " + line + ":" + charPositionInLine + " " + msg); + } + + public void generatorError(String msg) { + errors.add("Error: " + msg); + } + + public List getErrors() { + return errors; + } +} \ No newline at end of file diff --git a/src/main/java/de/maishai/Compiler.java b/src/main/java/de/maishai/Compiler.java index 9f2c4ca..ebd7e31 100644 --- a/src/main/java/de/maishai/Compiler.java +++ b/src/main/java/de/maishai/Compiler.java @@ -23,23 +23,27 @@ public class Compiler { public static Program generateAST(List fromSources) { List classes = new ArrayList<>(); + CollectingErrorListener errorListener = new CollectingErrorListener(); for (String fromSource : fromSources) { CharStream input = CharStreams.fromString(fromSource); DecafLexer lexer = new DecafLexer(input); //add custom error listener lexer.removeErrorListeners(); - lexer.addErrorListener(ThrowingErrorListener.INSTANCE); + lexer.addErrorListener(errorListener); CommonTokenStream tokens = new CommonTokenStream(lexer); DecafParser parser = new DecafParser(tokens); //add custom error listener parser.removeErrorListeners(); - parser.addErrorListener(ThrowingErrorListener.INSTANCE); + parser.addErrorListener(errorListener); DecafParser.ClassContext tree = parser.class_(); //Parsen - classes.add(ASTGenerator.generateAST(tree)); + classes.add(ASTGenerator.generateAST(tree, errorListener)); + } + for (String error : errorListener.getErrors()) { + LOGGER.severe(error); } return new Program(classes); } diff --git a/src/main/java/de/maishai/ExpressionGenerator.java b/src/main/java/de/maishai/ExpressionGenerator.java index 8b87f84..79bd7f0 100644 --- a/src/main/java/de/maishai/ExpressionGenerator.java +++ b/src/main/java/de/maishai/ExpressionGenerator.java @@ -11,9 +11,15 @@ import java.util.ArrayList; import java.util.List; public class ExpressionGenerator extends DecafBaseVisitor { + private final CollectingErrorListener errorListener; + + public ExpressionGenerator(CollectingErrorListener errorListener) { + this.errorListener = errorListener; + } + @Override public Expression visitBinaryOperation(DecafParser.BinaryOperationContext ctx) { - return generateBinary(ctx); + return generateBinary(ctx, errorListener); } @Override @@ -25,12 +31,13 @@ public class ExpressionGenerator extends DecafBaseVisitor { if (ctx.unaryOp().SUB() != null) { return new Unary(UnaryOperator.SUB, expr); } - throw new RuntimeException("No unary operator found."); + errorListener.generatorError("No unary operator found"); + return null; } @Override public Expression visitConstant(DecafParser.ConstantContext ctx) { - return generateConstant(ctx.literal()); + return generateConstant(ctx.literal(), errorListener); } @Override @@ -46,7 +53,7 @@ public class ExpressionGenerator extends DecafBaseVisitor { Expression recipient = null; if (ctx.fieldVarAccess().recipient() != null) { List recipientList = ctx.fieldVarAccess().recipient(); - recipient = ExpressionGenerator.generateRecursiveOwnerChain(recipientList, ctx.fieldVarAccess().newCall() != null ? StatementGenerator.generateNew(ctx.fieldVarAccess().newCall()) : null, isField); + recipient = ExpressionGenerator.generateRecursiveOwnerChain(recipientList, ctx.fieldVarAccess().newCall() != null ? StatementGenerator.generateNew(ctx.fieldVarAccess().newCall(), errorListener) : null, isField, errorListener); } if (recipient == null) { return new FieldVarAccess(isField, null, ctx.fieldVarAccess().id().IDENTIFIER().getText()); @@ -54,7 +61,7 @@ public class ExpressionGenerator extends DecafBaseVisitor { return new FieldVarAccess(true, recipient, ctx.fieldVarAccess().id().IDENTIFIER().getText()); } - public static Expression generateConstant(DecafParser.LiteralContext ctx) { + public static Expression generateConstant(DecafParser.LiteralContext ctx, CollectingErrorListener errorListener) { if (ctx.NUMBER() != null) { return new IntLiteral(Integer.valueOf(ctx.NUMBER().getText())); } @@ -63,17 +70,19 @@ public class ExpressionGenerator extends DecafBaseVisitor { } if (ctx.CHARLITERAL() != null) { if (ctx.CHARLITERAL().getText().length() != 3) { - throw new RuntimeException("Wrong format for Char literal. Good format: 'c' Bad format: " + ctx.CHARLITERAL().getText()); + errorListener.generatorError("Wrong format for Char literal. Good format: 'c' Bad format: " + ctx.CHARLITERAL().getText()); + return null; } return new CharLiteral(ctx.CHARLITERAL().getText().charAt(1)); } - throw new RuntimeException("No literal found!"); + errorListener.generatorError("No literal found"); + return null; } - public static Binary generateBinary(DecafParser.BinaryOperationContext ctx) { - ExpressionGenerator eGen = new ExpressionGenerator(); + public static Binary generateBinary(DecafParser.BinaryOperationContext ctx, CollectingErrorListener errorListener) { + ExpressionGenerator eGen = new ExpressionGenerator(errorListener); Binary binary = new Binary(eGen.visit(ctx.expr().get(0)) // left side - , generateOperator(ctx.binaryOp()) //operator + , generateOperator(ctx.binaryOp(), errorListener) //operator , eGen.visit(ctx.expr().get(1))); //right side binary = pointBeforeLineLogic(ctx, binary); @@ -81,7 +90,7 @@ public class ExpressionGenerator extends DecafBaseVisitor { return binary; } - public static Operator generateOperator(DecafParser.BinaryOpContext ctx) { + public static Operator generateOperator(DecafParser.BinaryOpContext ctx, CollectingErrorListener errorListener) { if (ctx.ADD() != null) return Operator.ADD; if (ctx.SUB() != null) return Operator.SUB; if (ctx.MUL() != null) return Operator.MUL; @@ -95,7 +104,8 @@ public class ExpressionGenerator extends DecafBaseVisitor { if (ctx.NE() != null) return Operator.NE; if (ctx.AND() != null) return Operator.AND; if (ctx.OR() != null) return Operator.OR; - throw new RuntimeException("No operator found!"); + errorListener.generatorError("No operator found"); + return null; } @Override @@ -109,7 +119,7 @@ public class ExpressionGenerator extends DecafBaseVisitor { Expression recursiveOwnerChain = null; if (ctx.methCall().recipient() != null) { List recipientList = ctx.methCall().recipient(); - recursiveOwnerChain = ExpressionGenerator.generateRecursiveOwnerChain(recipientList, ctx.methCall().newCall() != null ? StatementGenerator.generateNew(ctx.methCall().newCall()) : null, isField); + recursiveOwnerChain = ExpressionGenerator.generateRecursiveOwnerChain(recipientList, ctx.methCall().newCall() != null ? StatementGenerator.generateNew(ctx.methCall().newCall(), errorListener) : null, isField, errorListener); } List args = new ArrayList<>(); if (ctx.methCall().methName().args() != null) { @@ -126,11 +136,11 @@ public class ExpressionGenerator extends DecafBaseVisitor { @Override public Expression visitNew(DecafParser.NewContext ctx) { - return StatementGenerator.generateNew(ctx.newCall()); + return StatementGenerator.generateNew(ctx.newCall(), errorListener); } - public static Expression generateRecursiveOwnerChain(List ctxList, Expression recipient, Boolean isField) { + public static Expression generateRecursiveOwnerChain(List ctxList, Expression recipient, Boolean isField, CollectingErrorListener errorListener) { if (ctxList.isEmpty()) { return recipient; } @@ -141,23 +151,24 @@ public class ExpressionGenerator extends DecafBaseVisitor { if (ctxList.isEmpty()) { return new FieldVarAccess(isField, recipient, ctx.id().IDENTIFIER().getText()); } - return new FieldVarAccess(true, generateRecursiveOwnerChain(ctxList, recipient, isField), ctx.id().IDENTIFIER().getText()); + return new FieldVarAccess(true, generateRecursiveOwnerChain(ctxList, recipient, isField, errorListener), ctx.id().IDENTIFIER().getText()); } if (ctx.methName() != null) { List args = new ArrayList<>(); if (ctx.methName().args() != null) { for (var expr : ctx.methName().args().expr()) { - Expression astExpr = expr.accept(new ExpressionGenerator()); + Expression astExpr = expr.accept(new ExpressionGenerator(errorListener)); args.add(astExpr); } } if (ctxList.isEmpty()) { return new MethodCall(new FieldVarAccess(isField, recipient, ctx.methName().id().IDENTIFIER().getText()), args); } - return new MethodCall(new FieldVarAccess(true, generateRecursiveOwnerChain(ctxList, recipient, isField), ctx.methName().id().IDENTIFIER().getText()), args); + return new MethodCall(new FieldVarAccess(true, generateRecursiveOwnerChain(ctxList, recipient, isField, errorListener), ctx.methName().id().IDENTIFIER().getText()), args); } - throw new RuntimeException(); + errorListener.generatorError("Unknown Recipient while generating Recursive Owner Chain"); + return null; } private static Binary pointBeforeLineLogic(DecafParser.BinaryOperationContext ctx, Binary binary) { diff --git a/src/main/java/de/maishai/StatementGenerator.java b/src/main/java/de/maishai/StatementGenerator.java index 65bded7..29fef20 100644 --- a/src/main/java/de/maishai/StatementGenerator.java +++ b/src/main/java/de/maishai/StatementGenerator.java @@ -10,18 +10,24 @@ import java.util.ArrayList; import java.util.List; public class StatementGenerator extends DecafBaseVisitor> { + private final CollectingErrorListener errorListener; + + public StatementGenerator(CollectingErrorListener errorListener) { + this.errorListener = errorListener; + } + @Override public List visitIf(DecafParser.IfContext ctx) { - Expression expr = new ExpressionGenerator().visit(ctx.ifCall().expr()); - Block ifBlock = new BlockGenerator().visit(ctx.ifCall().block(0)); + Expression expr = new ExpressionGenerator(errorListener).visit(ctx.ifCall().expr()); + Block ifBlock = new BlockGenerator(errorListener).visit(ctx.ifCall().block(0)); Block elseBlock = null; if (ctx.ifCall().block().size() == 2) { - elseBlock = new BlockGenerator().visit(ctx.ifCall().block(1)); + elseBlock = new BlockGenerator(errorListener).visit(ctx.ifCall().block(1)); } if (ctx.ifCall().elseIf() != null){ for (int i = ctx.ifCall().elseIf().size()-1; i >= 0; i--) { - IfElse current = new IfElse(new ExpressionGenerator().visit(ctx.ifCall().elseIf(i).expr()), - new BlockGenerator().visit(ctx.ifCall().elseIf(i).block()), + IfElse current = new IfElse(new ExpressionGenerator(errorListener).visit(ctx.ifCall().elseIf(i).expr()), + new BlockGenerator(errorListener).visit(ctx.ifCall().elseIf(i).block()), elseBlock); elseBlock = new Block(List.of(current)); } @@ -33,12 +39,12 @@ public class StatementGenerator extends DecafBaseVisitor> { @Override public List visitFor(DecafParser.ForContext ctx) { - Expression expr = new ExpressionGenerator().visit(ctx.expr()); + Expression expr = new ExpressionGenerator(errorListener).visit(ctx.expr()); Assignment update = generateAssign(ctx.assign().get(ctx.assign().size() - 1)); - Block block = new BlockGenerator().visit(ctx.block()); + Block block = new BlockGenerator(errorListener).visit(ctx.block()); if (ctx.assign().size() == 1) { Declaration declaration = new Declaration(ctx.localVarWithInitialization().id().IDENTIFIER().getText(), ASTGenerator.getType(ctx.localVarWithInitialization().type())); - Expression initialization = new ExpressionGenerator().visit(ctx.localVarWithInitialization().expr()); + Expression initialization = new ExpressionGenerator(errorListener).visit(ctx.localVarWithInitialization().expr()); Assignment init = new Assignment(new FieldVarAccess(false, null, declaration.name()), initialization); return List.of(declaration, new For(init, expr, update, block)); } @@ -48,15 +54,15 @@ public class StatementGenerator extends DecafBaseVisitor> { @Override public List visitWhile(DecafParser.WhileContext ctx) { - Expression expr = new ExpressionGenerator().visit(ctx.expr()); - Block block = new BlockGenerator().visit(ctx.block()); + Expression expr = new ExpressionGenerator(errorListener).visit(ctx.expr()); + Block block = new BlockGenerator(errorListener).visit(ctx.block()); return List.of(new While(expr, block)); } @Override public List visitDoWhile(DecafParser.DoWhileContext ctx) { - Block block = new BlockGenerator().visit(ctx.block()); - Expression expr = new ExpressionGenerator().visit(ctx.expr()); + Block block = new BlockGenerator(errorListener).visit(ctx.block()); + Expression expr = new ExpressionGenerator(errorListener).visit(ctx.expr()); return List.of(new DoWhile(block, expr)); } @@ -79,7 +85,7 @@ public class StatementGenerator extends DecafBaseVisitor> { @Override public List visitLocalVarDecWithInitialization(DecafParser.LocalVarDecWithInitializationContext ctx) { Declaration declaration = new Declaration(ctx.localVarWithInitialization().id().IDENTIFIER().getText(), ASTGenerator.getType(ctx.localVarWithInitialization().type())); - Expression initialization = new ExpressionGenerator().visit(ctx.localVarWithInitialization().expr()); + Expression initialization = new ExpressionGenerator(errorListener).visit(ctx.localVarWithInitialization().expr()); return List.of(declaration, new Assignment(new FieldVarAccess(false, null, declaration.name()), initialization)); } @@ -91,14 +97,14 @@ public class StatementGenerator extends DecafBaseVisitor> { @Override public List visitPrint(DecafParser.PrintContext ctx) { - return List.of(new Print(new ExpressionGenerator().visit(ctx.expr()))); + return List.of(new Print(new ExpressionGenerator(errorListener).visit(ctx.expr()))); } private Assignment generateAssign(DecafParser.AssignContext ctx) { FieldVarAccess fieldVarAccess; if (ctx.incrDecr() == null) { fieldVarAccess = generateField(ctx.fieldVarAccess()); - Expression expr = resolveFancyAssign(ctx.assignSign(), fieldVarAccess, new ExpressionGenerator().visit(ctx.expr())); + Expression expr = resolveFancyAssign(ctx.assignSign(), fieldVarAccess, new ExpressionGenerator(errorListener).visit(ctx.expr()), errorListener); return new Assignment(fieldVarAccess, expr); } fieldVarAccess = generateField(ctx.incrDecr().fieldVarAccess()); @@ -110,7 +116,7 @@ public class StatementGenerator extends DecafBaseVisitor> { Boolean isField = fieldIdContext.THIS() != null; Expression recipient = null; if (fieldIdContext.recipient() != null) { - recipient = ExpressionGenerator.generateRecursiveOwnerChain(fieldIdContext.recipient(), null, isField); + recipient = ExpressionGenerator.generateRecursiveOwnerChain(fieldIdContext.recipient(), null, isField, errorListener); } if (recipient == null) { return new FieldVarAccess(isField, null, fieldIdContext.id().IDENTIFIER().getText()); @@ -130,14 +136,14 @@ public class StatementGenerator extends DecafBaseVisitor> { List args = new ArrayList<>(); if (ctx.methCall().methName().args() != null) { for (var expr : ctx.methCall().methName().args().expr()) { - Expression astExpr = expr.accept(new ExpressionGenerator()); + Expression astExpr = expr.accept(new ExpressionGenerator(errorListener)); args.add(astExpr); } } Expression recursiveOwnerChain = null; if (ctx.methCall().recipient() != null) { List recipientList = ctx.methCall().recipient(); - recursiveOwnerChain = ExpressionGenerator.generateRecursiveOwnerChain(recipientList, ctx.methCall().newCall() != null ? generateNew(ctx.methCall().newCall()) : null, isField); + recursiveOwnerChain = ExpressionGenerator.generateRecursiveOwnerChain(recipientList, ctx.methCall().newCall() != null ? generateNew(ctx.methCall().newCall(), errorListener) : null, isField, errorListener); } if (recursiveOwnerChain == null) { return List.of(new MethodCall(new FieldVarAccess(isField, null, ctx.methCall().methName().id().IDENTIFIER().getText()), args)); @@ -147,22 +153,22 @@ public class StatementGenerator extends DecafBaseVisitor> { @Override public List visitNew(DecafParser.NewContext ctx) { - return List.of( generateNew(ctx.newCall())); + return List.of( generateNew(ctx.newCall(), errorListener)); } - public static New generateNew(DecafParser.NewCallContext ctx){ + public static New generateNew(DecafParser.NewCallContext ctx, CollectingErrorListener errorListener) { Type type = ASTGenerator.getType(ctx.type()); List args = new ArrayList<>(); if (ctx.args() != null) { for (var expr : ctx.args().expr()) { - Expression astExpr = expr.accept(new ExpressionGenerator()); + Expression astExpr = expr.accept(new ExpressionGenerator(errorListener)); args.add(astExpr); } } return new New(type, args); } - public static Expression resolveFancyAssign(DecafParser.AssignSignContext ctx, FieldVarAccess fieldVarAccess, Expression expression) { + public static Expression resolveFancyAssign(DecafParser.AssignSignContext ctx, FieldVarAccess fieldVarAccess, Expression expression, CollectingErrorListener errorListener) { if (ctx.ASSIGN() != null) return expression; if (ctx.ADD_ASSIGN() != null) @@ -175,6 +181,7 @@ public class StatementGenerator extends DecafBaseVisitor> { return new Binary(fieldVarAccess, Operator.DIV, expression); if (ctx.MOD_ASSIGN() != null) return new Binary(fieldVarAccess, Operator.MOD, expression); - throw new RuntimeException("No assign sign found!"); + errorListener.generatorError("No assign sign found"); + return null; } } diff --git a/src/main/java/de/maishai/ThrowingErrorListener.java b/src/main/java/de/maishai/ThrowingErrorListener.java deleted file mode 100644 index 7cbbdee..0000000 --- a/src/main/java/de/maishai/ThrowingErrorListener.java +++ /dev/null @@ -1,17 +0,0 @@ -package de.maishai; - -import org.antlr.v4.runtime.BaseErrorListener; -import org.antlr.v4.runtime.RecognitionException; -import org.antlr.v4.runtime.Recognizer; -import org.antlr.v4.runtime.misc.ParseCancellationException; - -public class ThrowingErrorListener extends BaseErrorListener { - - public static final ThrowingErrorListener INSTANCE = new ThrowingErrorListener(); - - @Override - public void syntaxError(Recognizer recognizer, Object offendingSymbol, int line, int charPositionInLine, String msg, RecognitionException e) - throws ParseCancellationException { - throw new ParseCancellationException("Error in line " + line + ":" + charPositionInLine + " " + msg); - } -}