From a17201d10ae784b7bd8dff52d895d22b367aa418 Mon Sep 17 00:00:00 2001 From: ChUrl Date: Thu, 4 Feb 2021 16:17:33 +0100 Subject: [PATCH] overhaul logging --- src/main/java/StupsCompiler.java | 20 ++-- .../java/codegen/analysis/StackModel.java | 2 + .../codegen/analysis/StackSizeAnalyzer.java | 6 +- .../analysis/dataflow/DataFlowGraph.java | 5 + .../analysis/liveness/InterferenceGraph.java | 6 +- .../analysis/liveness/LivenessAnalysis.java | 16 +-- .../java/codegen/flowgraph/FlowGraph.java | 21 +++- .../codegen/flowgraph/FlowGraphGenerator.java | 41 ++++--- src/main/java/parser/ParseException.java | 5 +- src/main/java/parser/StupsParser.java | 35 +++--- .../java/parser/ast/ParseTreeCleaner.java | 50 +++++---- src/main/java/parser/ast/SyntaxTree.java | 1 - .../java/parser/ast/SyntaxTreeRebalancer.java | 37 +++--- src/main/java/parser/grammar/Grammar.java | 28 +++-- .../java/parser/grammar/GrammarAnalyzer.java | 76 +++++++------ .../parser/grammar/GrammarParseException.java | 4 + src/main/java/typechecker/TypeChecker.java | 40 ++++--- src/main/java/typechecker/TypeTable.java | 14 +-- src/main/java/util/Logger.java | 106 ++++++++++++++++-- 19 files changed, 334 insertions(+), 179 deletions(-) diff --git a/src/main/java/StupsCompiler.java b/src/main/java/StupsCompiler.java index ecd85c1..b69ea2e 100644 --- a/src/main/java/StupsCompiler.java +++ b/src/main/java/StupsCompiler.java @@ -39,13 +39,13 @@ public final class StupsCompiler { } private static void compile(String filename) { - System.out.println("Beginning compilation."); - final long begin = System.nanoTime(); + System.out.println("Kompiliere " + filename); +// final long begin = System.nanoTime(); final FlowGraphGenerator gen = getFlowGraphGen(filename); final FlowGraph graph = gen.generateGraph(); - Logger.call(graph::printToImage); + Logger.logInfoSupplier(graph::printToImage, StupsCompiler.class); // Codegeneration + Output final String fileExtension = filename.substring(filename.lastIndexOf('.') + 1); @@ -68,8 +68,9 @@ public final class StupsCompiler { return; } - final long end = System.nanoTime(); - System.out.printf("%nCompilation completed in %dms.%n", (end - begin) / 1_000_000); + System.out.println("Kompilieren abgeschlossen."); +// final long end = System.nanoTime(); +// System.out.printf("%nCompilation completed in %dms.%n", (end - begin) / 1_000_000); } private static void liveness(String filename) { @@ -78,14 +79,17 @@ public final class StupsCompiler { final FlowGraphGenerator gen = getFlowGraphGen(filename); final FlowGraph graph = gen.generateGraph(); - Logger.call(graph::printToImage); + Logger.logInfoSupplier(graph::printToImage, StupsCompiler.class); final DataFlowGraph dataFlowGraph = DataFlowGraph.fromFlowGraph(graph); - Logger.call(dataFlowGraph::printToImage); + Logger.logInfoSupplier(dataFlowGraph::printToImage, StupsCompiler.class); final LivenessAnalysis liveness = LivenessAnalysis.fromDataFlowGraph(dataFlowGraph, gen.getVarMap()); - liveness.doLivenessAnalysis(); + final int registers = liveness.doLivenessAnalysis(); + + System.out.println("Liveness-Analyse abgeschlossen."); + System.out.println("Registers: " + registers); } private static FlowGraphGenerator getFlowGraphGen(String filename) { diff --git a/src/main/java/codegen/analysis/StackModel.java b/src/main/java/codegen/analysis/StackModel.java index 3c9bb89..4959f7c 100644 --- a/src/main/java/codegen/analysis/StackModel.java +++ b/src/main/java/codegen/analysis/StackModel.java @@ -1,6 +1,7 @@ package codegen.analysis; import parser.ast.SyntaxTreeNode; +import util.Logger; import java.util.ArrayDeque; import java.util.Deque; @@ -37,6 +38,7 @@ public class StackModel { private void updateMax() { if (this.stack.size() > this.max) { this.max = this.stack.size(); + Logger.logInfo(" :: New maximum: " + this.max, StackModel.class); } } diff --git a/src/main/java/codegen/analysis/StackSizeAnalyzer.java b/src/main/java/codegen/analysis/StackSizeAnalyzer.java index 3a54fc0..a07a72c 100644 --- a/src/main/java/codegen/analysis/StackSizeAnalyzer.java +++ b/src/main/java/codegen/analysis/StackSizeAnalyzer.java @@ -2,11 +2,10 @@ package codegen.analysis; import parser.ast.SyntaxTree; import parser.ast.SyntaxTreeNode; +import util.Logger; import java.util.Set; -import static util.Logger.log; - /** * Ermittelt die maximal benötigte Stacktiefe für ein Programm. * Das Programm wird übergeben als {@link SyntaxTree}. @@ -24,12 +23,13 @@ public final class StackSizeAnalyzer { private StackSizeAnalyzer() {} public static int runStackModel(SyntaxTree tree) { - log("\nDetermining required stack depth:"); + Logger.logDebug("Determining minimal stack-depth", StackSizeAnalyzer.class); final StackModel stack = new StackModel(); runStackModel(tree.getRoot().getChildren().get(3).getChildren().get(11), stack); + Logger.logDebug("Found required stack-depth", StackSizeAnalyzer.class); return stack.getMax(); } diff --git a/src/main/java/codegen/analysis/dataflow/DataFlowGraph.java b/src/main/java/codegen/analysis/dataflow/DataFlowGraph.java index 385282b..3845fda 100644 --- a/src/main/java/codegen/analysis/dataflow/DataFlowGraph.java +++ b/src/main/java/codegen/analysis/dataflow/DataFlowGraph.java @@ -4,6 +4,7 @@ import codegen.flowgraph.FlowBasicBlock; import codegen.flowgraph.FlowGraph; import codegen.flowgraph.FlowInstruction; import util.GraphvizCaller; +import util.Logger; import java.util.ArrayList; import java.util.Collections; @@ -25,6 +26,8 @@ public final class DataFlowGraph implements Iterable { } public static DataFlowGraph fromFlowGraph(FlowGraph flowGraph) { + Logger.logDebug("Beginning data-flow-graph generation", DataFlowGraph.class); + final List dataFlowNodes = new ArrayList<>(); // Initialize all DataFlowNodes @@ -37,6 +40,8 @@ public final class DataFlowGraph implements Iterable { final DataFlowGraph dataFlowGraph = new DataFlowGraph(dataFlowNodes); initNodePosition(flowGraph, dataFlowGraph); + Logger.logDebug("Successfully generated data-flow-graph", DataFlowGraph.class); + return dataFlowGraph; } diff --git a/src/main/java/codegen/analysis/liveness/InterferenceGraph.java b/src/main/java/codegen/analysis/liveness/InterferenceGraph.java index 0a76dda..3460adc 100644 --- a/src/main/java/codegen/analysis/liveness/InterferenceGraph.java +++ b/src/main/java/codegen/analysis/liveness/InterferenceGraph.java @@ -21,6 +21,8 @@ public final class InterferenceGraph implements Iterable { } public static InterferenceGraph fromDataFlowGraph(DataFlowGraph dataFlowGraph, Map varMap) { + Logger.logDebug("Generating interference-graph", InterferenceGraph.class); + final List interferenceNodes = new ArrayList<>(); // Init graph @@ -41,13 +43,15 @@ public final class InterferenceGraph implements Iterable { if (leftNode.isPresent() && rightNode.isPresent()) { final boolean change = leftNode.get().addNeighbour(rightNode.get()); - Logger.logIfTrue(change, "Added interference neighbour: " + left + " -> " + right); + Logger.logInfoIfTrue(change, "Added interference neighbour: " + left + " -> " + right, InterferenceGraph.class); } } } } + Logger.logDebug("Successfully generated interference-graph", InterferenceGraph.class); + return interferenceGraph; } diff --git a/src/main/java/codegen/analysis/liveness/LivenessAnalysis.java b/src/main/java/codegen/analysis/liveness/LivenessAnalysis.java index 10a3db8..5beaf2f 100644 --- a/src/main/java/codegen/analysis/liveness/LivenessAnalysis.java +++ b/src/main/java/codegen/analysis/liveness/LivenessAnalysis.java @@ -30,6 +30,8 @@ public final class LivenessAnalysis { } private static void calculateLivenessInOut(DataFlowGraph dataFlowGraph) { + Logger.logDebug("Calculating in/out-sets", LivenessAnalysis.class); + boolean change; do { @@ -45,6 +47,8 @@ public final class LivenessAnalysis { change = change || calculateLivenessInOutNode(node); } } while (change); + + Logger.logDebug("Successfully calculated in/out-sets", LivenessAnalysis.class); } private static boolean calculateLivenessInOutNode(DataFlowNode dataFlowNode) { @@ -70,15 +74,11 @@ public final class LivenessAnalysis { * Die Registeranzahl wird durch naive Färbung des InterferenzGraphen ermittelt. */ public int doLivenessAnalysis() { - final int registers = this.colorInterferenceGraph(); - - System.out.println("\nRegisters: " + registers); - - return registers; + return this.colorInterferenceGraph(); } private int colorInterferenceGraph() { - Logger.log("Coloring Interference Graph\n"); + Logger.logDebug("Coloring interference-graph", LivenessAnalysis.class); int colors = 0; int currentColor; @@ -103,9 +103,11 @@ public final class LivenessAnalysis { if (currentColor > colors) { colors = currentColor; } + + Logger.logDebug("Successfully colored interference-graph", LivenessAnalysis.class); } - Logger.call(this.interferenceGraph::printToImage); + Logger.logInfoSupplier(this.interferenceGraph::printToImage, LivenessAnalysis.class); return colors; } diff --git a/src/main/java/codegen/flowgraph/FlowGraph.java b/src/main/java/codegen/flowgraph/FlowGraph.java index 9e583f9..54c7fb6 100644 --- a/src/main/java/codegen/flowgraph/FlowGraph.java +++ b/src/main/java/codegen/flowgraph/FlowGraph.java @@ -48,10 +48,14 @@ public class FlowGraph implements Iterable { * und zu Blöcken aus der {@link #predecessorMap} hergestellt. */ public void addLabel(String label) { + Logger.logInfo("Adding Label: " + label, FlowGraph.class); + final FlowBasicBlock newBlock = new FlowBasicBlock(label); // Resolve missing successors/predecessors from jumps if (this.predecessorMap.containsKey(label)) { + Logger.logInfo("Handling PredecessorMap Entry: " + this.predecessorMap.get(label), FlowGraph.class); + this.predecessorMap.get(label).addSuccessorBlock(newBlock); newBlock.addPredecessorBlock(this.predecessorMap.get(label)); } @@ -75,6 +79,8 @@ public class FlowGraph implements Iterable { * @param jumpInstruction Der verwendete Sprungbefehl. */ public void addJump(String jumpInstruction, String label) { + Logger.logInfo("Adding Jump to Label: " + label, FlowGraph.class); + this.addInstruction(jumpInstruction, label); final FlowBasicBlock newBlock = new FlowBasicBlock(); @@ -105,6 +111,7 @@ public class FlowGraph implements Iterable { // Successor doesn't exist, so wait until it does // Current node is predecessor of label-block + Logger.logInfo("Adding Entry to PredecessorMap: " + currentBlock, FlowGraph.class); currentBlock.ifPresent(flowBasicBlock -> this.predecessorMap.put(label, flowBasicBlock)); } @@ -112,6 +119,8 @@ public class FlowGraph implements Iterable { } public void addInstruction(String instruction, String... args) { + Logger.logInfo("Adding Instruction: " + instruction, FlowGraph.class); + if (this.basicBlocks.isEmpty()) { this.basicBlocks.add(new FlowBasicBlock("START")); // First block doesn't exist } @@ -127,14 +136,14 @@ public class FlowGraph implements Iterable { * Ein Block ist "leer", wenn er kein Label und keine Instructions hat. */ public void purgeEmptyBlocks() { - Logger.log("\nPurging empty blocks: "); + Logger.logDebug("Purging empty blocks", FlowGraph.class); final Collection toRemove = new HashSet<>(); // Collect removable blocks for (FlowBasicBlock block : this.basicBlocks) { if (block.isEmpty()) { - Logger.log("Marking Block " + this.basicBlocks.indexOf(block) + " as removable."); + Logger.logInfo("Marking Block " + this.basicBlocks.indexOf(block) + " as removable.", FlowGraph.class); toRemove.add(block); } } @@ -146,8 +155,8 @@ public class FlowGraph implements Iterable { for (FlowBasicBlock predecessor : block.getBlockPredecessorSet()) { for (FlowBasicBlock successor : block.getBlockSuccessorSet()) { - Logger.log("Rerouting Block " + this.basicBlocks.indexOf(predecessor) - + " to Block " + this.basicBlocks.indexOf(successor)); + Logger.logInfo("Rerouting Block " + this.basicBlocks.indexOf(predecessor) + + " to Block " + this.basicBlocks.indexOf(successor), FlowGraph.class); predecessor.addSuccessorBlock(successor); successor.addPredecessorBlock(predecessor); } @@ -164,6 +173,8 @@ public class FlowGraph implements Iterable { } this.basicBlocks.removeAll(toRemove); + + Logger.logDebug("Successfully removed all empty blocks and rerouted graph", FlowGraph.class); } private Optional getBlockByLabel(String label) { @@ -198,8 +209,6 @@ public class FlowGraph implements Iterable { .append("node[shape=Mrecord]\n"); for (FlowBasicBlock block : this.basicBlocks) { - System.out.println(block); - System.out.println("-".repeat(100)); dot.append(block.getId()) .append(" [label=\"{ ") .append(this.basicBlocks.indexOf(block)) diff --git a/src/main/java/codegen/flowgraph/FlowGraphGenerator.java b/src/main/java/codegen/flowgraph/FlowGraphGenerator.java index c931267..fed0bfd 100644 --- a/src/main/java/codegen/flowgraph/FlowGraphGenerator.java +++ b/src/main/java/codegen/flowgraph/FlowGraphGenerator.java @@ -5,6 +5,7 @@ import codegen.analysis.StackSizeAnalyzer; import parser.ast.SyntaxTree; import parser.ast.SyntaxTreeNode; import typechecker.TypeChecker; +import util.Logger; import java.util.ArrayDeque; import java.util.Collections; @@ -12,8 +13,6 @@ import java.util.Deque; import java.util.HashMap; import java.util.Map; -import static util.Logger.log; - /** * Erzeugt den SourceCode in FlussGraph-Darstellung. */ @@ -58,6 +57,8 @@ public final class FlowGraphGenerator { } private static Map initVarMap(SyntaxTree tree) { + Logger.logDebug("Initializing variable-map", FlowGraphGenerator.class); + final Map varMap = new HashMap<>(); final Deque stack = new ArrayDeque<>(); @@ -74,14 +75,16 @@ public final class FlowGraphGenerator { currentVarNumber++; varMap.put(current.getChildren().get(0).getValue(), currentVarNumber); - log("New local " + current.getValue() + " variable " - + current.getChildren().get(0).getValue() - + " assigned to slot " + currentVarNumber + "."); + Logger.logInfo("New local " + current.getValue() + " variable " + + current.getChildren().get(0).getValue() + + " assigned to slot " + currentVarNumber + ".", FlowGraphGenerator.class); } current.getChildren().forEach(stack::push); } + Logger.logDebug("Successfully initialized variable-map", FlowGraphGenerator.class); + return Collections.unmodifiableMap(varMap); } @@ -100,14 +103,14 @@ public final class FlowGraphGenerator { * Die Instruktionen sind unterteilt in BasicBlocks, welche über Kanten verbunden sind. */ public FlowGraph generateGraph() { - System.out.println(" - Generating Source Graph..."); + Logger.logDebug("Beginning generation of source-graph", FlowGraphGenerator.class); // Skip the first 2 identifiers: ClassName, MainArgs this.generateNode(this.tree.getRoot().getChildren().get(3).getChildren().get(11)); this.graph.purgeEmptyBlocks(); - log("\n\nSourceGraph print:\n" + "-".repeat(100) + "\n" + this.graph + "-".repeat(100)); - System.out.println("Graph-generation successful."); + Logger.logDebug("Source-graph generation complete", FlowGraphGenerator.class); + Logger.logInfo("\n\nSourceGraph print:\n" + "-".repeat(100) + "\n" + this.graph + "-".repeat(100), FlowGraphGenerator.class); return this.graph; } @@ -135,6 +138,8 @@ public final class FlowGraphGenerator { * Erzeugt den Teilbaum für einen If-Knoten. */ private void condNode(SyntaxTreeNode root) { + Logger.logInfo("Generating Conditional Node", FlowGraphGenerator.class); + final int currentLabel = this.labelCounter; this.labelCounter++; @@ -164,6 +169,8 @@ public final class FlowGraphGenerator { * Erzeugt den Teilbaum für einen While-Knoten. */ private void loopNode(SyntaxTreeNode root) { + Logger.logInfo("Generating Loop Node", FlowGraphGenerator.class); + final int currentLabel = this.labelCounter; this.labelCounter++; @@ -189,6 +196,8 @@ public final class FlowGraphGenerator { * Die JVM-Stacksize wird dabei um 1 verringert, da istore/astore 1 Argument konsumieren. */ private void assignNode(SyntaxTreeNode root) { //! Stack - 1 + Logger.logInfo("Generating Assignment Node", FlowGraphGenerator.class); + this.generateNode(root.getChildren().get(0)); final String type = this.nodeTypeMap.get(root.getChildren().get(0)); @@ -198,7 +207,7 @@ public final class FlowGraphGenerator { default -> throw new IllegalStateException("Unexpected value: " + type); }; - log("assign(): " + root.getName() + ": " + root.getValue() + " => " + inst); + Logger.logInfo("assign(): " + root.getName() + ": " + root.getValue() + " => " + inst, FlowGraphGenerator.class); this.graph.addInstruction(inst, this.varMap.get(root.getValue()).toString()); } @@ -220,6 +229,8 @@ public final class FlowGraphGenerator { * bei binären Operatoren sinkt die Stackgröße um 1 (2 konsumiert, 1 Ergebnis). */ private void intExpr(SyntaxTreeNode root) { + Logger.logInfo("Generating Integer Expression Node", FlowGraphGenerator.class); + String inst = ""; if (root.getChildren().size() == 1) { //! Stack + 0 @@ -248,7 +259,7 @@ public final class FlowGraphGenerator { }; } - log("intExpr(): " + root.getName() + ": " + root.getValue() + " => " + inst); + Logger.logInfo("intExpr(): " + root.getName() + ": " + root.getValue() + " => " + inst, FlowGraphGenerator.class); this.graph.addInstruction(inst); } @@ -259,6 +270,8 @@ public final class FlowGraphGenerator { * bei binären Operatoren sinkt die Stackgröße um 1 (2 konsumiert, 1 Ergebnis). */ private void boolExpr(SyntaxTreeNode node) { + Logger.logInfo("Generating Boolean Expression", FlowGraphGenerator.class); + if (node.getChildren().size() == 1) { //! Stack + 1 // Unary operator @@ -329,14 +342,14 @@ public final class FlowGraphGenerator { // Leafs private void intStringLiteralNode(SyntaxTreeNode node) { //! Stack + 1 - log("intStringLiteral(): " + node.getName() + ": " + node.getValue() + " => ldc"); + Logger.logInfo("intStringLiteral(): " + node.getName() + ": " + node.getValue() + " => ldc", FlowGraphGenerator.class); // bipush only pushes 1 byte as int this.graph.addInstruction("ldc", node.getValue()); } private void boolLiteralNode(SyntaxTreeNode node) { //! Stack + 1 - log("booleanLiteral(): " + node.getName() + ": " + node.getValue() + " => ldc"); + Logger.logInfo("booleanLiteral(): " + node.getName() + ": " + node.getValue() + " => ldc", FlowGraphGenerator.class); final String val = "true".equals(node.getValue()) ? "1" : "0"; @@ -351,7 +364,7 @@ public final class FlowGraphGenerator { default -> throw new IllegalStateException("Unexpected value: " + type); }; - log("identifier(): " + node.getName() + ": " + node.getValue() + " => " + inst); + Logger.logInfo("identifier(): " + node.getName() + ": " + node.getValue() + " => " + inst, FlowGraphGenerator.class); this.graph.addInstruction(inst, this.varMap.get(node.getValue()).toString()); } @@ -369,7 +382,7 @@ public final class FlowGraphGenerator { this.generateNode(expr); - log("println(): " + expr.getName() + ": " + expr.getValue() + " => " + type); + Logger.logInfo("println(): " + expr.getName() + ": " + expr.getValue() + " => " + type, FlowGraphGenerator.class); this.graph.addInstruction("invokevirtual", "java/io/PrintStream/println(" + type + ")V"); } diff --git a/src/main/java/parser/ParseException.java b/src/main/java/parser/ParseException.java index fee9877..e3888fe 100644 --- a/src/main/java/parser/ParseException.java +++ b/src/main/java/parser/ParseException.java @@ -1,14 +1,13 @@ package parser; import parser.ast.SyntaxTree; - -import static util.Logger.log; +import util.Logger; public class ParseException extends RuntimeException { public ParseException(String message, SyntaxTree syntaxTree) { super("\n" + message); - log("\nAST at last state:\n" + syntaxTree); + Logger.logException("\nAST at last state:\n" + syntaxTree, ParseException.class); } } diff --git a/src/main/java/parser/StupsParser.java b/src/main/java/parser/StupsParser.java index c95fea7..fa7bb13 100644 --- a/src/main/java/parser/StupsParser.java +++ b/src/main/java/parser/StupsParser.java @@ -6,6 +6,7 @@ import parser.ast.SyntaxTree; import parser.ast.SyntaxTreeNode; import parser.grammar.Grammar; import parser.grammar.GrammarAnalyzer; +import util.Logger; import java.util.ArrayDeque; import java.util.Collection; @@ -13,8 +14,9 @@ import java.util.Deque; import java.util.List; import java.util.Optional; -import static util.Logger.log; - +/** + * Leitet eine Liste von Token nach einer Grammatik mit Hilfe einer {@link ParsingTable} ab. + */ public class StupsParser { private final ParsingTable parsetable; @@ -28,17 +30,18 @@ public class StupsParser { return new StupsParser(analyzer.getTable()); } - private static void printSourceLine(int line, Collection token) { + private static String printSourceLine(int line, Collection token) { final Optional srcLine = token.stream() .filter(tok -> tok.getLine() == line) .map(Token::getText) .reduce((s1, s2) -> s1 + " " + s2); - srcLine.ifPresent(s -> System.out.println(" :: " + s)); + return " :: " + srcLine.orElse(""); } public SyntaxTree parse(List token, Vocabulary voc) { - System.out.println(" - Parsing program..."); + Logger.logDebug("Beginning program-parsing", StupsParser.class); + final SyntaxTreeNode root = new SyntaxTreeNode(Grammar.START_SYMBOL, 0); final SyntaxTree tree = new SyntaxTree(root); final Deque stack = new ArrayDeque<>(); @@ -46,13 +49,14 @@ public class StupsParser { int inputPosition = 0; - log("\nParsing:"); - log("Input: " + token + "\n"); + Logger.logInfo("Input: " + token + "\n", StupsParser.class); // Parsing while (!stack.isEmpty()) { final String top = stack.peek().getName(); + Logger.logInfo("Parsing Top Symbol: " + top, StupsParser.class); + final String currentTokenSym; int currentLine = 0; if (inputPosition >= token.size()) { @@ -81,22 +85,23 @@ public class StupsParser { } else if (this.parsetable.getTerminals().contains(top)) { // Wenn das Terminal auf dem Stack nicht mit der aktuellen Eingabe übereinstimmt - System.out.println("\nLine " + currentLine + " Syntaxerror: Expected " + top + " but found " + currentTokenSym); - StupsParser.printSourceLine(currentLine, token); + Logger.logError("\nLine " + currentLine + " Syntaxerror: Expected " + top + " but found " + + currentTokenSym, StupsParser.class); + Logger.logError(StupsParser.printSourceLine(currentLine, token), StupsParser.class); throw new ParseException("Invalid terminal on stack: " + top, tree); } else if (prod == null) { // Wenn es für das aktuelle Terminal und das Nichtterminal auf dem Stack keine Regel gibt - System.out.println("\nLine " + currentLine + " Syntaxerror: Didn't expect " + currentTokenSym); - StupsParser.printSourceLine(currentLine, token); + Logger.logError("\nLine " + currentLine + " Syntaxerror: Didn't expect " + currentTokenSym, StupsParser.class); + Logger.logError(StupsParser.printSourceLine(currentLine, token), StupsParser.class); throw new ParseException("No prod. for nonterminal " + top + ", terminal " + currentTokenSym, tree); } else { // Wenn das Nichtterminal auf dem Stack durch (s)eine Produktion ersetzt werden kann // Hier wird auch der AST aufgebaut - log("Used: " + top + " -> " + prod); + Logger.logInfo("Used: " + top + " -> " + prod, StupsParser.class); final SyntaxTreeNode pop = stack.pop(); final String[] split = prod.split(" "); @@ -120,10 +125,8 @@ public class StupsParser { } } - log("\nParsed AST:\n" + tree); - log("-".repeat(100) + "\n"); - - System.out.println("Parsing successful."); + Logger.logDebug("Successfully parsed the program and built the parse-tree", StupsParser.class); + Logger.logInfo("\nParsed AST:\n" + tree, StupsParser.class); return tree; } diff --git a/src/main/java/parser/ast/ParseTreeCleaner.java b/src/main/java/parser/ast/ParseTreeCleaner.java index 81417c8..9ffc064 100644 --- a/src/main/java/parser/ast/ParseTreeCleaner.java +++ b/src/main/java/parser/ast/ParseTreeCleaner.java @@ -1,12 +1,11 @@ package parser.ast; import parser.grammar.Grammar; +import util.Logger; import java.util.Collection; import java.util.HashSet; -import static util.Logger.log; - /** * Wendet in der Grammatik definierte Regeln auf einen Parsebaum an. * Dies ist der erste Schritt zum Abstrakten Syntaxbaum. @@ -24,6 +23,8 @@ public final class ParseTreeCleaner { private ParseTreeCleaner() {} public static void clean(SyntaxTree parseTree, Grammar grammar) { + Logger.logDebug("Beginning cleaning of parse-tree", ParseTreeCleaner.class); + deleteChildren(parseTree, grammar); deleteIfEmpty(parseTree, grammar); promote(parseTree, grammar); @@ -32,17 +33,17 @@ public final class ParseTreeCleaner { nameToValue(parseTree, grammar); valueToValue(parseTree, grammar); - log("\nCleaned Tree:\n" + parseTree); - log("-".repeat(100)); - System.out.println(" - Compressing syntax-tree..."); + Logger.logDebug("Successfully cleaned the parse-tree", ParseTreeCleaner.class); + Logger.logInfo("\nCleaned Tree:\n" + parseTree, ParseTreeCleaner.class); } /** * Es werden Werte nach oben gereicht von [promote]-able Nodes. */ public static void promote(SyntaxTree parseTree, Grammar grammar) { - log("\nPromoting nodes:"); + Logger.logDebug(" :: Beginning up-propagation of nodes", ParseTreeCleaner.class); promote(parseTree.getRoot(), grammar); + Logger.logDebug(" :: Promoted nodes", ParseTreeCleaner.class); } private static void promote(SyntaxTreeNode root, Grammar grammar) { @@ -56,8 +57,8 @@ public final class ParseTreeCleaner { continue; } - log("Promoting " + child.getName() + " -> " + root.getName()); - log(root.toString()); + Logger.logInfo("Promoting " + child.getName() + " -> " + root.getName(), ParseTreeCleaner.class); + Logger.logInfo(root.toString(), ParseTreeCleaner.class); root.setName(child.getName()); root.setValue(child.getValue()); @@ -74,8 +75,9 @@ public final class ParseTreeCleaner { * Löscht leere Knoten mit [delIfEmpty]. */ public static void deleteIfEmpty(SyntaxTree parseTree, Grammar grammar) { - log("\nDeleting empty nodes:"); + Logger.logDebug(" :: Beginning removal of empty nodes", ParseTreeCleaner.class); deleteIfEmpty(parseTree.getRoot(), grammar); + Logger.logDebug(" :: Removed all empty nodes", ParseTreeCleaner.class); } private static void deleteIfEmpty(SyntaxTreeNode root, Grammar grammar) { @@ -88,7 +90,7 @@ public final class ParseTreeCleaner { continue; } - log("Removing " + child.getName()); + Logger.logInfo("Removing " + child.getName(), ParseTreeCleaner.class); child.setValue("REMOVE"); // If both childs have the same identity both are removed, so change one toRemove.add(child); @@ -101,8 +103,9 @@ public final class ParseTreeCleaner { * Löscht redundante Informationen in [delChildren]-Nodes (z.b. IF-child von COND) und Epsilon-Nodes. */ public static void deleteChildren(SyntaxTree parseTree, Grammar grammar) { - log("Removing redundant children:"); + Logger.logDebug(" :: Beginning removal of redundant children", ParseTreeCleaner.class); deleteChildren(parseTree.getRoot(), grammar); + Logger.logDebug(" :: Redundant children were removed", ParseTreeCleaner.class); } private static void deleteChildren(SyntaxTreeNode root, Grammar grammar) { @@ -115,7 +118,7 @@ public final class ParseTreeCleaner { continue; } - log("Removing " + root.getName() + " -> " + child.getName()); + Logger.logInfo("Removing " + root.getName() + " -> " + child.getName(), ParseTreeCleaner.class); child.setValue("REMOVE"); // If both childs have the same identity both are removed, so change one toRemove.add(child); @@ -128,8 +131,9 @@ public final class ParseTreeCleaner { * Führt Umbenennungen durch. */ private static void renameTo(SyntaxTree parseTree, Grammar grammar) { - log("\nRenaming nodes:"); + Logger.logDebug(" :: Beginning renaming of nodes", ParseTreeCleaner.class); renameTo(parseTree.getRoot(), grammar); + Logger.logDebug(" :: Renamed nodes", ParseTreeCleaner.class); } private static void renameTo(SyntaxTreeNode root, Grammar grammar) { @@ -140,7 +144,7 @@ public final class ParseTreeCleaner { continue; } - log("Rename " + root.getName() + " to " + grammar.getNewName(root) + "."); + Logger.logInfo("Rename " + root.getName() + " to " + grammar.getNewName(root) + ".", ParseTreeCleaner.class); root.setName(grammar.getNewName(root)); } @@ -150,8 +154,9 @@ public final class ParseTreeCleaner { * Verschiebt Knotennamen von [nametoval]-Nodes in Parent-Values und löscht das Child. */ public static void nameToValue(SyntaxTree parseTree, Grammar grammar) { - log("\nMoving names to values:"); + Logger.logDebug(" :: Beginning up-propagation of node-names", ParseTreeCleaner.class); nameToValue(parseTree.getRoot(), grammar); + Logger.logDebug(" :: Moved node-names to parent-values", ParseTreeCleaner.class); } private static void nameToValue(SyntaxTreeNode root, Grammar grammar) { @@ -164,8 +169,8 @@ public final class ParseTreeCleaner { continue; } - log("Moving " + child.getName() + " to value of " + root.getName()); - log(root.toString()); + Logger.logInfo("Moving " + child.getName() + " to value of " + root.getName(), ParseTreeCleaner.class); + Logger.logInfo(root.toString(), ParseTreeCleaner.class); root.setValue(child.getName()); @@ -182,8 +187,9 @@ public final class ParseTreeCleaner { * Variablennamen als Wert anstatt als Child-Node. */ public static void valueToValue(SyntaxTree parseTree, Grammar grammar) { - log("\nMoving values to values:"); + Logger.logDebug(" :: Beginning up-propagation of node-values", ParseTreeCleaner.class); valueToValue(parseTree.getRoot(), grammar); + Logger.logDebug(" :: Moved node-values to parent-values", ParseTreeCleaner.class); } private static void valueToValue(SyntaxTreeNode root, Grammar grammar) { @@ -200,8 +206,8 @@ public final class ParseTreeCleaner { && root.getChildren().get(0).getName().equals(root.getChildren().get(1).getName())) { // Case where variable is assigned another variable with the same name - log("Moving " + root.getChildren().get(1).getValue() + " to value of " + root.getName()); - log(root.toString()); + Logger.logInfo("Moving " + root.getChildren().get(1).getValue() + " to value of " + root.getName(), ParseTreeCleaner.class); + Logger.logInfo(root.toString(), ParseTreeCleaner.class); root.setValue(root.getChildren().get(1).getValue()); @@ -211,8 +217,8 @@ public final class ParseTreeCleaner { } else { // Usual case where an expression is assigned - log("Moving " + child.getValue() + " to value of " + root.getName()); - log(root.toString()); + Logger.logInfo("Moving " + child.getValue() + " to value of " + root.getName(), ParseTreeCleaner.class); + Logger.logInfo(root.toString(), ParseTreeCleaner.class); root.setValue(child.getValue()); toRemove.add(child); diff --git a/src/main/java/parser/ast/SyntaxTree.java b/src/main/java/parser/ast/SyntaxTree.java index cd41079..7293218 100644 --- a/src/main/java/parser/ast/SyntaxTree.java +++ b/src/main/java/parser/ast/SyntaxTree.java @@ -25,7 +25,6 @@ public class SyntaxTree { ParseTreeCleaner.clean(abstractSyntaxTree, grammar); SyntaxTreeRebalancer.rebalance(abstractSyntaxTree); - System.out.println("Tree processing successful."); return abstractSyntaxTree; } diff --git a/src/main/java/parser/ast/SyntaxTreeRebalancer.java b/src/main/java/parser/ast/SyntaxTreeRebalancer.java index 02854b9..0908ff0 100644 --- a/src/main/java/parser/ast/SyntaxTreeRebalancer.java +++ b/src/main/java/parser/ast/SyntaxTreeRebalancer.java @@ -1,11 +1,11 @@ package parser.ast; +import util.Logger; + import java.util.Collections; import java.util.Map; import java.util.Set; -import static util.Logger.log; - /** * Ein SyntaxTree wird an bestimmten Stellen rotiert, sodass bestimmte Eigenschaften * korrekt repräsentiert werden (Operatorpräzedenz, Linkassoziativität etc.). @@ -62,23 +62,24 @@ public final class SyntaxTreeRebalancer { * */ public static void rebalance(SyntaxTree abstractSyntaxTree) { + Logger.logDebug("Beginning rebalancing of syntax-tree", SyntaxTreeRebalancer.class); + flip(abstractSyntaxTree); leftPrecedence(abstractSyntaxTree); operatorPrecedence(abstractSyntaxTree); flipCommutativeExpr(abstractSyntaxTree); - log(abstractSyntaxTree.toString()); - log("-".repeat(100)); - - System.out.println(" - Balancing syntax-tree..."); + Logger.logDebug("Successfully rebalanced syntax-tree", SyntaxTreeRebalancer.class); + Logger.logInfo("AST after rebalancing:" + abstractSyntaxTree, SyntaxTreeRebalancer.class); } /** * Baum spiegeln, damit höhere Ebenen links sind und EXPR vorwärts laufen. */ public static void flip(SyntaxTree abstractSyntaxTree) { - log("Flipping tree for ltr evaluation"); + Logger.logDebug(" :: Flipping tree for ltr evaluation", SyntaxTreeRebalancer.class); flip(abstractSyntaxTree.getRoot()); + Logger.logDebug(" :: Successfully flipped tree", SyntaxTreeRebalancer.class); } private static void flip(SyntaxTreeNode root) { @@ -93,8 +94,9 @@ public final class SyntaxTreeRebalancer { * Kommutative Ausdrücke werden gespiegelt, damit die tiefen Teilexpressions zuerst berechnet werden. */ public static void flipCommutativeExpr(SyntaxTree abstractSyntaxTree) { - log("Flipping commutative expressions for stack efficiency"); + Logger.logDebug(" :: Flipping commutative expressions for stack efficiency", SyntaxTreeRebalancer.class); flipCommutativeExpr(abstractSyntaxTree.getRoot()); + Logger.logDebug(" :: Succesfully optimized stack efficiency", SyntaxTreeRebalancer.class); } private static void flipCommutativeExpr(SyntaxTreeNode root) { @@ -108,8 +110,8 @@ public final class SyntaxTreeRebalancer { if (root.getChildren().size() == 2 && root.getChildren().get(0).size() < root.getChildren().get(1).size()) { // Make the bigger subtree the left one - log("Flipping " + root.getName() + ": " + root.getValue() + " for stack efficiency."); - log(root.toString()); + Logger.logInfo("Flipping " + root.getName() + ": " + root.getValue() + " for stack efficiency.", SyntaxTreeRebalancer.class); + Logger.logInfo(root.toString(), SyntaxTreeRebalancer.class); Collections.reverse(root.getChildren()); } @@ -121,8 +123,9 @@ public final class SyntaxTreeRebalancer { * Es werden EXPR-Nodes (2 Childs, 1 davon EXPR, Kein Wert) solange wie möglich linksrotiert. */ public static void leftPrecedence(SyntaxTree abstractSyntaxTree) { - log("Left-rotating expressions for left-precedence"); + Logger.logDebug(" :: Left-rotating expressions for left-precedence", SyntaxTreeRebalancer.class); leftPrecedence(abstractSyntaxTree.getRoot()); + Logger.logDebug(" :: Successfully rotated expressions for left-precedence", SyntaxTreeRebalancer.class); } private static void leftPrecedence(SyntaxTreeNode root) { @@ -150,8 +153,8 @@ public final class SyntaxTreeRebalancer { * @return Es wird false zurückgegeben, sobald keine weitere Rotation mehr möglich ist. */ private static boolean specialLeftRotate(SyntaxTreeNode root) { - log("Special-Left-Rotation around " + root.getName()); - log(root.toString()); + Logger.logInfo("Special-Left-Rotation around " + root.getName(), SyntaxTreeRebalancer.class); + Logger.logInfo(root.toString(), SyntaxTreeRebalancer.class); final SyntaxTreeNode left = root.getChildren().get(0); final SyntaxTreeNode right = root.getChildren().get(1); @@ -195,13 +198,15 @@ public final class SyntaxTreeRebalancer { * als die Operatoren mit niedriger Priorität. */ public static void operatorPrecedence(SyntaxTree abstractSyntaxTree) { - log("Right-rotating expressions for operator-precedence"); + Logger.logDebug(" :: Right-rotating expressions for operator-precedence", SyntaxTreeRebalancer.class); boolean changed; do { changed = operatorPrecedence(abstractSyntaxTree.getRoot()); } while (changed); + + Logger.logDebug(" :: Rotated expressions for operator-precedence", SyntaxTreeRebalancer.class); } public static boolean operatorPrecedence(SyntaxTreeNode root) { @@ -240,8 +245,8 @@ public final class SyntaxTreeRebalancer { } private static void simpleRightRotate(SyntaxTreeNode root) { - log("Right-Rotation around " + root.getName() + ": " + root.getValue()); - log(root.toString()); + Logger.logInfo("Right-Rotation around " + root.getName() + ": " + root.getValue(), SyntaxTreeRebalancer.class); + Logger.logInfo(root.toString(), SyntaxTreeRebalancer.class); final SyntaxTreeNode left = root.getChildren().get(0); final SyntaxTreeNode right = root.getChildren().get(1); diff --git a/src/main/java/parser/grammar/Grammar.java b/src/main/java/parser/grammar/Grammar.java index 983ade5..e2823b8 100644 --- a/src/main/java/parser/grammar/Grammar.java +++ b/src/main/java/parser/grammar/Grammar.java @@ -1,6 +1,7 @@ package parser.grammar; import parser.ast.SyntaxTreeNode; +import util.Logger; import java.io.IOException; import java.nio.file.Files; @@ -22,7 +23,6 @@ import static parser.grammar.GrammarAction.NAMETOVAL; import static parser.grammar.GrammarAction.PROMOTE; import static parser.grammar.GrammarAction.RENAMETO; import static parser.grammar.GrammarAction.VALTOVAL; -import static util.Logger.log; /** * Repräsentiert die Parse-Grammatik und die Kontextaktionen. @@ -90,7 +90,6 @@ public class Grammar { } public static Grammar fromFile(Path path) throws IOException { - System.out.println(" - Reading parser-grammar..."); List lines = Files.readAllLines(path); // Remove Whitespace + Comments @@ -116,10 +115,10 @@ public class Grammar { actionMap.put(action, new HashSet<>()); } - log("Parsing Grammar from File:"); + Logger.logDebug("Beginning grammar parsing", Grammar.class); for (String currentLine : lines) { - log("Parsed: " + currentLine); + Logger.logInfo("Parsed: " + currentLine, Grammar.class); // Parse Keywords if (currentLine.startsWith("TERM:")) { @@ -137,9 +136,8 @@ public class Grammar { } } - log("\n" + actionMap); - log("-".repeat(100)); - System.out.println("Grammar parsed successfully."); + Logger.logInfo("Registered actions: " + actionMap, Grammar.class); + Logger.logDebug("Grammar parsed successfully", Grammar.class); return new Grammar(terminals, nonterminals, actionMap, renameMappings, nameToValMappings, @@ -172,6 +170,8 @@ public class Grammar { final Set actionSet = parseActionSet(leftside, open, close); + Logger.logInfo("Current Line " + currentLine + " has Actions: " + actionSet, Grammar.class); + // Validate Actions throwOnInvalidActionSet(actionSet); @@ -214,7 +214,7 @@ public class Grammar { final String[] flagSplit = flag.split("="); final GrammarAction action = GrammarAction.valueOf(flagSplit[0].toUpperCase()); - registerRegularAction(action, leftside, flag, actions); + registerAction(action, leftside, flag, actions); if (flagSplit.length > 1) { @@ -226,11 +226,11 @@ public class Grammar { /** * Es wird ein Eintrag in der action-Map mit der entsprechenden leftside hinzugefügt. */ - private static void registerRegularAction(GrammarAction action, String leftside, String flag, - Map> actions) { + private static void registerAction(GrammarAction action, String leftside, String flag, + Map> actions) { actions.get(action).add(leftside.trim()); - log("Registered " + flag + ": " + leftside.trim()); + Logger.logInfo("Registered " + flag + ": " + leftside.trim(), Grammar.class); } /** @@ -246,6 +246,8 @@ public class Grammar { final int argStart = flag.indexOf('='); final String[] argSplit = flag.substring(argStart + 1).split(","); + Logger.logInfo("Registered " + flag + " args: " + argSplit, Grammar.class); + switch (action) { case DELCHILD -> delChildMappings.put(leftside, Arrays.asList(argSplit)); case VALTOVAL -> valToValMappings.put(leftside, Arrays.asList(argSplit)); @@ -267,6 +269,8 @@ public class Grammar { for (String prod : prods) { final GrammarRule rule = new GrammarRule(leftside, prod.split(" ")); rules.add(rule); + + Logger.logInfo("Registered production " + rule, Grammar.class); } } @@ -277,6 +281,8 @@ public class Grammar { for (String flag : flagSet) { if (!actionSet.contains(flag.split("=")[0].toUpperCase())) { + + Logger.logError("Action " + flag.split("=")[0] + " is invalid.", Grammar.class); throw new GrammarParseException("Invalid Action: " + flag); } } diff --git a/src/main/java/parser/grammar/GrammarAnalyzer.java b/src/main/java/parser/grammar/GrammarAnalyzer.java index 4c056e0..9d0fc52 100644 --- a/src/main/java/parser/grammar/GrammarAnalyzer.java +++ b/src/main/java/parser/grammar/GrammarAnalyzer.java @@ -1,6 +1,7 @@ package parser.grammar; import parser.ParsingTable; +import util.Logger; import java.util.AbstractMap; import java.util.Arrays; @@ -12,10 +13,6 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; -import static util.Logger.log; -import static util.Logger.logIfTrue; -import static util.Logger.logNullable; - public final class GrammarAnalyzer { private final Grammar grammar; @@ -37,17 +34,14 @@ public final class GrammarAnalyzer { private GrammarAnalyzer(Grammar grammar) { this.grammar = grammar; - log("Analyzing Grammar:\n"); + Logger.logDebug("Beginning grammar analysis", GrammarAnalyzer.class); - // Es muss zwingend in der Reihenfolge [Nullable < First < Follow < Table] initialisiert werden - System.out.println(" - Initializing first-set..."); + // Es muss zwingend in der Reihenfolge [First < Follow < Table] initialisiert werden this.first = this.initFirst(); - System.out.println(" - Initializing follow-set..."); this.follow = this.initFollow(); - System.out.println(" - Initializing parse-table..."); this.table = this.initParseTable(); - System.out.println("Grammar analysis successful."); + Logger.logDebug("Grammar analysis successful", GrammarAnalyzer.class); } public static GrammarAnalyzer fromGrammar(Grammar grammar) { @@ -55,6 +49,8 @@ public final class GrammarAnalyzer { } private Map> initFirst() { + Logger.logDebug(" :: Initializing first-set", GrammarAnalyzer.class); + final Map> firstOut = new HashMap<>(); // Die Methode funktioniert erst, nachdem first initialisiert ist. @@ -65,8 +61,6 @@ public final class GrammarAnalyzer { final Predicate allNullable = split -> split.length == 0 || Arrays.stream(split).allMatch(nullable); - log("First Initialisieren:"); - // Initialisieren for (String nterm : this.grammar.getNonterminals()) { firstOut.put(nterm, new HashSet<>()); @@ -113,7 +107,8 @@ public final class GrammarAnalyzer { final boolean changeNow = firstOut.get(leftside).addAll(firstYiNoEps); change = change || changeNow; - logIfTrue(changeNow, "First: Added " + firstYiNoEps + " to " + leftside + " (All before are nullable)"); + Logger.logInfoIfTrue(changeNow, "First: Added " + firstYiNoEps + " to " + + leftside + " (All before are nullable)", GrammarAnalyzer.class); } if (i == split.length - 1 && allNullable.test(split)) { @@ -122,7 +117,8 @@ public final class GrammarAnalyzer { final boolean changeNow = firstOut.get(leftside).add(Grammar.EPSILON_SYMBOL); change = change || changeNow; - logIfTrue(changeNow, "First: Added " + Grammar.EPSILON_SYMBOL + " to " + leftside + " (All are nullable)"); + Logger.logInfoIfTrue(changeNow, "First: Added " + Grammar.EPSILON_SYMBOL + " to " + + leftside + " (All are nullable)", GrammarAnalyzer.class); } } } @@ -133,22 +129,23 @@ public final class GrammarAnalyzer { final boolean changeNow = firstOut.get(leftside).add(Grammar.EPSILON_SYMBOL); change = change || changeNow; - logIfTrue(changeNow, "First: Added " + Grammar.EPSILON_SYMBOL + " to " + leftside + " (X -> EPS exists)"); + Logger.logInfoIfTrue(changeNow, "First: Added " + Grammar.EPSILON_SYMBOL + " to " + + leftside + " (X -> EPS exists)", GrammarAnalyzer.class); } } } } while (change); - log("\n" + firstOut); - log("-".repeat(100) + "\n"); + Logger.logDebug(" :: First-set initialized successfully", GrammarAnalyzer.class); + Logger.logInfo("First Set: " + firstOut, GrammarAnalyzer.class); return firstOut; } private Map> initFollow() { - final Map> followOut = new HashMap<>(); + Logger.logDebug(" :: Initializing follow-set", GrammarAnalyzer.class); - log("Follow Initialisieren:"); + final Map> followOut = new HashMap<>(); // Initialisieren for (String nterm : this.grammar.getNonterminals()) { @@ -195,7 +192,8 @@ public final class GrammarAnalyzer { final boolean changeNow = followOut.get(split[i - 1]).addAll(firstXkNoEps); change = change || changeNow; - logIfTrue(changeNow, "Follow: Added " + firstXkNoEps + " to " + split[i - 1] + " (Dazwischen nullable)"); + Logger.logInfoIfTrue(changeNow, "Follow: Added " + firstXkNoEps + " to " + + split[i - 1] + " (Dazwischen nullable)", GrammarAnalyzer.class); } } @@ -208,7 +206,8 @@ public final class GrammarAnalyzer { final boolean changeNow = followOut.get(split[i - 1]).addAll(followOut.get(leftside)); change = change || changeNow; - logIfTrue(changeNow, "Follow: Added " + leftside + " to " + split[i - 1] + " (Dahinter nullable)"); + Logger.logInfoIfTrue(changeNow, "Follow: Added " + leftside + " to " + + split[i - 1] + " (Dahinter nullable)", GrammarAnalyzer.class); } } @@ -218,23 +217,24 @@ public final class GrammarAnalyzer { final boolean changeNow = followOut.get(split[split.length - 1]).addAll(followOut.get(leftside)); change = change || changeNow; - logIfTrue(changeNow, "Follow: Added " + followOut.get(leftside) + " to " + split[split.length - 1] + " (Ende der Regel)"); + Logger.logInfoIfTrue(changeNow, "Follow: Added " + followOut.get(leftside) + " to " + + split[split.length - 1] + " (Ende der Regel)", GrammarAnalyzer.class); } } } } while (change); - log("\n" + followOut); - log("-".repeat(100) + "\n"); + Logger.logDebug(" :: Follow-set initialized successfully", GrammarAnalyzer.class); + Logger.logInfo("Follow Set: " + followOut, GrammarAnalyzer.class); return followOut; } private ParsingTable initParseTable() { - final Map, String> tableOut = new HashMap<>(); + Logger.logDebug(" :: Initializing parse-table", GrammarAnalyzer.class); - log("Parsetable Aufstellen:"); + final Map, String> tableOut = new HashMap<>(); for (String leftside : this.grammar.getLeftSides()) { @@ -248,8 +248,9 @@ public final class GrammarAnalyzer { final String prev = tableOut.put(new AbstractMap.SimpleEntry<>(leftside, sym), rightside); - log("Add " + rightside + " to cell (" + leftside + ", " + sym + ") (" + sym + " in first of " + rightside + ")"); - logNullable("Overwritten " + prev + "!\n", prev); + Logger.logInfo("Add " + rightside + " to cell (" + leftside + ", " + sym + ") (" + sym + + " in first of " + rightside + ")", GrammarAnalyzer.class); + Logger.logInfoNullable(prev, "Overwritten " + prev + "!\n", GrammarAnalyzer.class); } final Set followLeftside = this.follow(leftside); @@ -262,8 +263,9 @@ public final class GrammarAnalyzer { final String prev = tableOut.put(new AbstractMap.SimpleEntry<>(leftside, sym), rightside); - log("Add " + rightside + " to cell (" + leftside + ", " + sym + ") (" + sym + " in follow of " + leftside + ")"); - logNullable("Overwritten " + prev + "!\n", prev); + Logger.logInfo("Add " + rightside + " to cell (" + leftside + ", " + sym + ") (" + sym + + " in follow of " + leftside + ")", GrammarAnalyzer.class); + Logger.logInfoNullable(prev, "Overwritten " + prev + "!\n", GrammarAnalyzer.class); } if (followLeftside.contains("$")) { @@ -271,19 +273,21 @@ public final class GrammarAnalyzer { final String prev = tableOut.put(new AbstractMap.SimpleEntry<>(leftside, "$"), rightside); - log("Add " + rightside + " to cell (" + leftside + ", $) (epsilon in first of " + rightside + " and $ in follow of " + leftside + ")"); - logNullable("Overwritten " + prev + "!\n", prev); + Logger.logInfo("Add " + rightside + " to cell (" + leftside + + ", $) (epsilon in first of " + rightside + " and $ in follow of " + + leftside + ")", GrammarAnalyzer.class); + Logger.logInfoNullable(prev, "Overwritten " + prev + "!\n", GrammarAnalyzer.class); } } } } - final ParsingTable table = new ParsingTable(this.grammar, tableOut); + final ParsingTable parsingTable = new ParsingTable(this.grammar, tableOut); - log("\n" + table); - log("-".repeat(100) + "\n"); + Logger.logDebug(" :: Parse-table initialized successfully", GrammarAnalyzer.class); + Logger.logInfo("ParsingTable: " + parsingTable, GrammarAnalyzer.class); - return table; + return parsingTable; } diff --git a/src/main/java/parser/grammar/GrammarParseException.java b/src/main/java/parser/grammar/GrammarParseException.java index 1ad92b5..dba91d7 100644 --- a/src/main/java/parser/grammar/GrammarParseException.java +++ b/src/main/java/parser/grammar/GrammarParseException.java @@ -1,8 +1,12 @@ package parser.grammar; +import util.Logger; + public class GrammarParseException extends RuntimeException { public GrammarParseException(String message) { super(message); + + Logger.logException(message, GrammarParseException.class); } } diff --git a/src/main/java/typechecker/TypeChecker.java b/src/main/java/typechecker/TypeChecker.java index 8a36750..e4d892a 100644 --- a/src/main/java/typechecker/TypeChecker.java +++ b/src/main/java/typechecker/TypeChecker.java @@ -2,6 +2,7 @@ package typechecker; import parser.ast.SyntaxTree; import parser.ast.SyntaxTreeNode; +import util.Logger; import java.util.Arrays; import java.util.Collection; @@ -9,8 +10,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static util.Logger.log; - public final class TypeChecker { private static final Collection lit = Arrays.asList("INTEGER_LIT", "STRING_LIT", "BOOLEAN_LIT"); @@ -19,18 +18,17 @@ public final class TypeChecker { private TypeChecker() {} // TODO: merge nodeTable into typetable? - // Wirft exception bei typeerror, return nodeTable? + // Wirft exception bei typeerror public static Map validate(SyntaxTree tree) { final TypeTable table = TypeTable.fromAST(tree); final Map nodeTable = new HashMap<>(); - System.out.println(" - Validating syntax-tree..."); + Logger.logDebug("Beginning typevalidation of abstract-syntax-tree", TypeChecker.class); - log("Typevalidation:"); validate(tree.getRoot(), table, nodeTable); - log("-".repeat(100)); - System.out.println("Typechecking successful."); + Logger.logDebug("Successfully typevalidated the abstract-syntax-tree", TypeChecker.class); + return nodeTable; } @@ -44,6 +42,8 @@ public final class TypeChecker { final String literalType = getLiteralType(root.getName()); + Logger.logInfo("Type " + literalType + " for Node:\n" + root, TypeChecker.class); + nodeTable.put(root, literalType); return; } else if ("expr".equals(root.getName())) { @@ -51,6 +51,8 @@ public final class TypeChecker { final String exprType = table.getMethodReturnType(root.getValue()); + Logger.logInfo("Type " + exprType + " for Node:\n" + root, TypeChecker.class); + nodeTable.put(root, exprType); } else if ("par_expr".equals(root.getName())) { // Nodetable Eintrag für Klammern @@ -63,6 +65,8 @@ public final class TypeChecker { final String identifierType = table.getSymbolType(root.getValue()); + Logger.logInfo("Type " + identifierType + " for Node:\n" + root, TypeChecker.class); + nodeTable.put(root, identifierType); } @@ -79,10 +83,11 @@ public final class TypeChecker { final SyntaxTreeNode literalNode = root.getChildren().get(0); final String literalType = nodeTable.get(literalNode); - log("Validating Assignment: " + identifierType + ": " + identifier + " = " + literalType); + Logger.logInfo("Validating Assignment: " + identifierType + ": " + identifier + " = " + literalType, TypeChecker.class); if (!literalType.equals(identifierType)) { - System.out.println("Line " + root.getLine() + " Typeerror: Can't assign [" + literalNode.getValue() + "] to [" + identifier + "]: " + identifierType); + Logger.logError("Line " + root.getLine() + " Typeerror: Can't assign [" + literalNode.getValue() + + "] to [" + identifier + "]: " + identifierType, TypeChecker.class); throw new AssignmentTypeMismatchException("Trying to assign " + literalType + " to a " + identifierType + " variable."); } @@ -91,25 +96,25 @@ public final class TypeChecker { private static void validateExpression(SyntaxTreeNode root, TypeTable table, Map nodeTable) { final String op = root.getValue(); - log("Validating Expression: " + root.getValue()); + Logger.logInfo("Validating Expression: " + root.getValue(), TypeChecker.class); if (root.isEmpty()) { // Keine Kinder - System.out.println("Line " + root.getLine() + " Operatorerror: Can't use [" + op + "] without arguments"); + Logger.logError("Line " + root.getLine() + " Operatorerror: Can't use [" + op + "] without arguments", TypeChecker.class); throw new OperatorUsageException("Versuche Operator " + op + " ohne Argumente aufzurufen."); } else if (root.getChildren().size() != 1 && "NOT".equals(op)) { // Unärer Operator mit != 1 Child // SUB, ADD müssen nicht geprüft werden, da diese doppelt belegt sind mit ihrem binären Gegenstück - System.out.println("Line " + root.getLine() + " Operatorerror: Can't use [" + op + "] with more than 1 argument"); + Logger.logError("Line " + root.getLine() + " Operatorerror: Can't use [" + op + "] with more than 1 argument", TypeChecker.class); throw new OperatorUsageException("Versuche unären Operator " + op + " mit mehreren Argument aufzurufen."); } else if (root.getChildren().size() == 1 && !unary.contains(op)) { // Binärer Operator mit 1 Child - System.out.println("Line " + root.getLine() + " Operatorerror: Can't use [" + op + "] with only 1 argument"); + Logger.logError("Line " + root.getLine() + " Operatorerror: Can't use [" + op + "] with only 1 argument", TypeChecker.class); throw new OperatorUsageException("Versuche binären Operator " + op + " mit einem Argument aufzurufen."); } @@ -121,7 +126,7 @@ public final class TypeChecker { final String childReturnType = nodeTable.get(child); if (childReturnType == null) { - System.out.println("Variable " + child.getValue() + " wurde nicht deklariert."); + Logger.logError("Variable " + child.getValue() + " wurde nicht deklariert.", TypeChecker.class); throw new SymbolNotDefinedException("Zugriff auf nicht deklarierte Variable " + child.getValue()); } @@ -130,7 +135,8 @@ public final class TypeChecker { // Child returned Typ, welcher nicht im SymbolTable als Argumenttyp steht // Der NodeTable enthält auch Literale, diese müssen also nicht einzeln behandelt werden - System.out.println("Line " + root.getLine() + " Typeerror: Can't use [" + op + "] with argument of type [" + nodeTable.get(child) + "]"); + Logger.logError("Line " + root.getLine() + " Typeerror: Can't use [" + op + + "] with argument of type [" + nodeTable.get(child) + "]", TypeChecker.class); throw new OperatorTypeMismatchException("Versuche Operator " + op + " mit Argument vom Typ " + nodeTable.get(child) + " aufzurufen."); } @@ -141,7 +147,9 @@ public final class TypeChecker { final SyntaxTreeNode right = root.getChildren().get(1); if (!nodeTable.get(left).equals(nodeTable.get(right))) { - System.out.println("Line " + root.getLine() + " Typeerror: Can't use [" + op + "] with arguments of type [" + nodeTable.get(left) + "] and [" + nodeTable.get(right) + "]"); + Logger.logError("Line " + root.getLine() + " Typeerror: Can't use [" + op + + "] with arguments of type [" + nodeTable.get(left) + "] and [" + nodeTable.get(right) + + "]", TypeChecker.class); throw new OperatorTypeMismatchException("Versuche Operator" + op + " mit Argumenten ungleichen Types zu verwenden."); } diff --git a/src/main/java/typechecker/TypeTable.java b/src/main/java/typechecker/TypeTable.java index 28305f5..25cd215 100644 --- a/src/main/java/typechecker/TypeTable.java +++ b/src/main/java/typechecker/TypeTable.java @@ -2,6 +2,7 @@ package typechecker; import parser.ast.SyntaxTree; import parser.ast.SyntaxTreeNode; +import util.Logger; import java.util.Arrays; import java.util.Collections; @@ -9,8 +10,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import static util.Logger.log; - /** * Speichert die Datentypen von Symbolen und Funktionen in einem Programm. */ @@ -68,12 +67,13 @@ public final class TypeTable { } public static TypeTable fromAST(SyntaxTree tree) { - System.out.println(" - Building TypeTable..."); + Logger.logDebug("Building typetable", TypeTable.class); + final Map symbolTable = new HashMap<>(); - log("Creating TypeTable"); initSymbolTable(tree.getRoot(), symbolTable); - log("-".repeat(100)); + + Logger.logDebug("Successfully built typetable", TypeTable.class); return new TypeTable(symbolTable); } @@ -86,11 +86,11 @@ public final class TypeTable { if ("declaration".equals(root.getName())) { final SyntaxTreeNode child = root.getChildren().get(0); - log("Adding Entry " + child.getValue() + " -> " + root.getValue()); + Logger.logInfo("Adding Entry " + child.getValue() + " -> " + root.getValue(), TypeTable.class); final String oldEntry = table.put(child.getValue(), root.getValue()); if (oldEntry != null) { - System.out.println("Line " + root.getLine() + " Symbolerror: [" + child.getValue() + "] already defined"); + Logger.logError("Line " + root.getLine() + " Symbolerror: [" + child.getValue() + "] already defined", TypeTable.class); throw new SymbolAlreadyDefinedException("Das Symbol " + child.getValue() + " wurde bereits deklariert."); } } diff --git a/src/main/java/util/Logger.java b/src/main/java/util/Logger.java index 7ece954..da088c5 100644 --- a/src/main/java/util/Logger.java +++ b/src/main/java/util/Logger.java @@ -1,35 +1,117 @@ package util; +import java.util.Map; import java.util.function.Supplier; // Maximal professioneller Logger public final class Logger { - private static final boolean enabled = false; + private static final boolean LOG_ENABLED = true; + private static final boolean LOG_EXCEPTIONS = false; + private static final int LOG_LEVEL = 1; // 0 = ERROR, 1 = DEBUG, 2 = INFO + + private static final Map packages; + + static { + packages = Map.ofEntries(Map.entry("parser.grammar", true), + Map.entry("parser", true), + Map.entry("parser.ast", true), + Map.entry("typechecker", true), + Map.entry("codegen.flowgraph", true), + Map.entry("codegen.analysis", true), + Map.entry("codegen.analysis.dataflow", true), + Map.entry("codegen.analysis.liveness", true), + Map.entry("codegen", false)); + } private Logger() {} - public static void log(String message) { - if (enabled) { - System.out.println(message); + private static void log(String message, Class clazz) { + if (LOG_ENABLED + && packages.containsKey(clazz.getPackageName()) && packages.get(clazz.getPackageName()).equals(true)) { + System.out.printf("%-75s\t(%s)%n", message, clazz.getName()); + + } else if (LOG_ENABLED && !packages.containsKey(clazz.getPackageName())) { + System.out.println("Failed Logging attempt from " + clazz.getName() + ": " + clazz.getPackageName()); } } - public static void logNullable(String message, String nullable) { - if (nullable != null && !nullable.isBlank() && !nullable.isEmpty() && !"null".equals(nullable)) { - log(message); + public static void logException(String message, Class clazz) { + if (LOG_EXCEPTIONS) { + log("EXCEP - " + message, clazz); } } - public static void logIfTrue(boolean pred, String message) { + public static void logError(String message, Class clazz) { + if (LOG_LEVEL >= 0) { + log("ERROR - " + message, clazz); + } + } + + public static void logDebug(String message, Class clazz) { + if (LOG_LEVEL >= 1) { + log("DEBUG - " + message, clazz); + } + } + + public static void logInfo(String message, Class clazz) { + if (LOG_LEVEL >= 2) { + log("INFO - " + message, clazz); + } + } + + public static void logErrorSupplier(Supplier call, Class clazz) { + if (LOG_ENABLED && LOG_LEVEL >= 0) { + logError(call.get(), clazz); + } + } + + public static void logDebugSupplier(Supplier call, Class clazz) { + if (LOG_ENABLED && LOG_LEVEL >= 1) { + logDebug(call.get(), clazz); + } + } + + public static void logInfoSupplier(Supplier call, Class clazz) { + if (LOG_ENABLED && LOG_LEVEL >= 2) { + logInfo(call.get(), clazz); + } + } + + // TODO: Flipped nullble and message + public static void logErrorNullable(String nullable, String message, Class clazz) { + if (nullable != null && !nullable.isEmpty() && !"null".equals(nullable)) { + logError(message, clazz); + } + } + + public static void logDebugNullable(String nullable, String message, Class clazz) { + if (nullable != null && !nullable.isEmpty() && !"null".equals(nullable)) { + logDebug(message, clazz); + } + } + + public static void logInfoNullable(String nullable, String message, Class clazz) { + if (nullable != null && !nullable.isEmpty() && !"null".equals(nullable)) { + logInfo(message, clazz); + } + } + + public static void logErrorIfTrue(boolean pred, String message, Class clazz) { if (pred) { - log(message); + logError(message, clazz); } } - public static void call(Supplier call) { - if (enabled) { - call.get(); + public static void logDebugIfTrue(boolean pred, String message, Class clazz) { + if (pred) { + logDebug(message, clazz); + } + } + + public static void logInfoIfTrue(boolean pred, String message, Class clazz) { + if (pred) { + logInfo(message, clazz); } } }