overhaul logging

This commit is contained in:
ChUrl
2021-02-04 16:17:33 +01:00
parent eb206f6629
commit a17201d10a
19 changed files with 334 additions and 179 deletions

View File

@ -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) {

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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<DataFlowNode> {
}
public static DataFlowGraph fromFlowGraph(FlowGraph flowGraph) {
Logger.logDebug("Beginning data-flow-graph generation", DataFlowGraph.class);
final List<DataFlowNode> dataFlowNodes = new ArrayList<>();
// Initialize all DataFlowNodes
@ -37,6 +40,8 @@ public final class DataFlowGraph implements Iterable<DataFlowNode> {
final DataFlowGraph dataFlowGraph = new DataFlowGraph(dataFlowNodes);
initNodePosition(flowGraph, dataFlowGraph);
Logger.logDebug("Successfully generated data-flow-graph", DataFlowGraph.class);
return dataFlowGraph;
}

View File

@ -21,6 +21,8 @@ public final class InterferenceGraph implements Iterable<InterferenceNode> {
}
public static InterferenceGraph fromDataFlowGraph(DataFlowGraph dataFlowGraph, Map<String, Integer> varMap) {
Logger.logDebug("Generating interference-graph", InterferenceGraph.class);
final List<InterferenceNode> interferenceNodes = new ArrayList<>();
// Init graph
@ -41,13 +43,15 @@ public final class InterferenceGraph implements Iterable<InterferenceNode> {
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;
}

View File

@ -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;
}

View File

@ -48,10 +48,14 @@ public class FlowGraph implements Iterable<FlowBasicBlock> {
* 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<FlowBasicBlock> {
* @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<FlowBasicBlock> {
// 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<FlowBasicBlock> {
}
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<FlowBasicBlock> {
* 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<FlowBasicBlock> 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<FlowBasicBlock> {
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<FlowBasicBlock> {
}
this.basicBlocks.removeAll(toRemove);
Logger.logDebug("Successfully removed all empty blocks and rerouted graph", FlowGraph.class);
}
private Optional<FlowBasicBlock> getBlockByLabel(String label) {
@ -198,8 +209,6 @@ public class FlowGraph implements Iterable<FlowBasicBlock> {
.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=\"{<f0> ")
.append(this.basicBlocks.indexOf(block))

View File

@ -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<String, Integer> initVarMap(SyntaxTree tree) {
Logger.logDebug("Initializing variable-map", FlowGraphGenerator.class);
final Map<String, Integer> varMap = new HashMap<>();
final Deque<SyntaxTreeNode> 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");
}

View File

@ -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);
}
}

View File

@ -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<? extends Token> token) {
private static String printSourceLine(int line, Collection<? extends Token> token) {
final Optional<String> 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<? extends Token> 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<SyntaxTreeNode> 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;
}

View File

@ -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);

View File

@ -25,7 +25,6 @@ public class SyntaxTree {
ParseTreeCleaner.clean(abstractSyntaxTree, grammar);
SyntaxTreeRebalancer.rebalance(abstractSyntaxTree);
System.out.println("Tree processing successful.");
return abstractSyntaxTree;
}

View File

@ -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 {
* </ul>
*/
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);

View File

@ -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<String> 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<String> 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<GrammarAction, Set<String>> actions) {
private static void registerAction(GrammarAction action, String leftside, String flag,
Map<GrammarAction, Set<String>> 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);
}
}

View File

@ -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<String, Set<String>> initFirst() {
Logger.logDebug(" :: Initializing first-set", GrammarAnalyzer.class);
final Map<String, Set<String>> firstOut = new HashMap<>();
// Die Methode funktioniert erst, nachdem first initialisiert ist.
@ -65,8 +61,6 @@ public final class GrammarAnalyzer {
final Predicate<String[]> 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<String, Set<String>> initFollow() {
final Map<String, Set<String>> followOut = new HashMap<>();
Logger.logDebug(" :: Initializing follow-set", GrammarAnalyzer.class);
log("Follow Initialisieren:");
final Map<String, Set<String>> 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<Map.Entry<String, String>, String> tableOut = new HashMap<>();
Logger.logDebug(" :: Initializing parse-table", GrammarAnalyzer.class);
log("Parsetable Aufstellen:");
final Map<Map.Entry<String, String>, 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<String> 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;
}

View File

@ -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);
}
}

View File

@ -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<String> 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<SyntaxTreeNode, String> validate(SyntaxTree tree) {
final TypeTable table = TypeTable.fromAST(tree);
final Map<SyntaxTreeNode, String> 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<SyntaxTreeNode, String> 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.");
}

View File

@ -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<String, String> 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.");
}
}

View File

@ -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<String, Boolean> 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<String> call, Class clazz) {
if (LOG_ENABLED && LOG_LEVEL >= 0) {
logError(call.get(), clazz);
}
}
public static void logDebugSupplier(Supplier<String> call, Class clazz) {
if (LOG_ENABLED && LOG_LEVEL >= 1) {
logDebug(call.get(), clazz);
}
}
public static void logInfoSupplier(Supplier<String> 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<String> 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);
}
}
}