From 108a2001e8a39f8d56d3afd3e0e932998192d0f4 Mon Sep 17 00:00:00 2001 From: ChUrl Date: Sun, 31 Jan 2021 18:39:41 +0100 Subject: [PATCH] rework SyntaxTree (AST) --- src/main/java/StupsCompiler.java | 13 +- src/main/java/codegen/CodeGenerator.java | 6 +- .../java/codegen/analysis/StackModel.java | 6 +- .../codegen/analysis/StackSizeAnalyzer.java | 22 +-- .../java/codegen/flowgraph/FlowGraph.java | 4 +- .../codegen/flowgraph/FlowGraphGenerator.java | 64 ++++----- src/main/java/parser/ParseException.java | 6 +- src/main/java/parser/StupsParser.java | 16 +-- src/main/java/parser/ast/AST.java | 57 -------- src/main/java/parser/ast/ASTBalancer.java | 52 +++---- src/main/java/parser/ast/ASTCompacter.java | 48 +++---- src/main/java/parser/ast/ASTNode.java | 126 ---------------- src/main/java/parser/ast/SyntaxTree.java | 74 ++++++++++ src/main/java/parser/ast/SyntaxTreeNode.java | 136 ++++++++++++++++++ src/main/java/parser/grammar/Grammar.java | 20 +-- src/main/java/typechecker/TypeChecker.java | 28 ++-- src/main/java/typechecker/TypeTable.java | 12 +- .../java/codegen/FlowGraphGeneratorTest.java | 36 ++--- .../liveness/LivenessAnalysisTest.java | 16 +-- src/test/java/parser/ast/ASTTest.java | 45 ------ ...rTest.java => SyntaxTreeBalancerTest.java} | 58 ++++---- ...Test.java => SyntaxTreeCompacterTest.java} | 16 +-- src/test/java/parser/ast/SyntaxTreeTest.java | 45 ++++++ .../parser/typechecker/TypeCheckerTest.java | 10 +- .../parser/typechecker/TypeTableTest.java | 18 +-- 25 files changed, 481 insertions(+), 453 deletions(-) delete mode 100644 src/main/java/parser/ast/AST.java delete mode 100644 src/main/java/parser/ast/ASTNode.java create mode 100644 src/main/java/parser/ast/SyntaxTree.java create mode 100644 src/main/java/parser/ast/SyntaxTreeNode.java delete mode 100644 src/test/java/parser/ast/ASTTest.java rename src/test/java/parser/ast/{ASTBalancerTest.java => SyntaxTreeBalancerTest.java} (65%) rename src/test/java/parser/ast/{ASTCompacterTest.java => SyntaxTreeCompacterTest.java} (75%) create mode 100644 src/test/java/parser/ast/SyntaxTreeTest.java diff --git a/src/main/java/StupsCompiler.java b/src/main/java/StupsCompiler.java index e2954f2..8c1f40a 100644 --- a/src/main/java/StupsCompiler.java +++ b/src/main/java/StupsCompiler.java @@ -6,8 +6,8 @@ import lexer.StupsLexer; import org.antlr.v4.runtime.CharStreams; import org.antlr.v4.runtime.Lexer; import parser.StupsParser; -import parser.ast.AST; -import parser.ast.ASTNode; +import parser.ast.SyntaxTree; +import parser.ast.SyntaxTreeNode; import parser.grammar.Grammar; import typechecker.TypeChecker; import util.Logger; @@ -122,10 +122,11 @@ public final class StupsCompiler { final StupsParser stupsParser = StupsParser.fromGrammar(grammar); // Parsing + Typechecking of program - final AST tree = stupsParser.parse(lexer.getAllTokens(), lexer.getVocabulary()); - tree.postprocess(grammar); - final Map nodeTable = TypeChecker.validate(tree); + final SyntaxTree parseTree = stupsParser.parse(lexer.getAllTokens(), lexer.getVocabulary()); - return FlowGraphGenerator.fromAST(tree, nodeTable, filename); + final SyntaxTree abstractSyntaxTree = SyntaxTree.toAbstractSyntaxTree(parseTree, grammar); + final Map nodeTable = TypeChecker.validate(abstractSyntaxTree); + + return FlowGraphGenerator.fromAST(abstractSyntaxTree, nodeTable, filename); } } diff --git a/src/main/java/codegen/CodeGenerator.java b/src/main/java/codegen/CodeGenerator.java index 21461a6..1ade8b2 100644 --- a/src/main/java/codegen/CodeGenerator.java +++ b/src/main/java/codegen/CodeGenerator.java @@ -2,8 +2,8 @@ package codegen; import codegen.flowgraph.FlowGraph; import codegen.flowgraph.FlowGraphGenerator; -import parser.ast.AST; -import parser.ast.ASTNode; +import parser.ast.SyntaxTree; +import parser.ast.SyntaxTreeNode; import java.util.Map; @@ -11,7 +11,7 @@ public final class CodeGenerator { private CodeGenerator() {} - public static String generateCode(AST tree, Map nodeTypeMap, String source) { + public static String generateCode(SyntaxTree tree, Map nodeTypeMap, String source) { final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTypeMap, source); final FlowGraph graph = gen.generateGraph(); diff --git a/src/main/java/codegen/analysis/StackModel.java b/src/main/java/codegen/analysis/StackModel.java index 34b944f..3c9bb89 100644 --- a/src/main/java/codegen/analysis/StackModel.java +++ b/src/main/java/codegen/analysis/StackModel.java @@ -1,6 +1,6 @@ package codegen.analysis; -import parser.ast.ASTNode; +import parser.ast.SyntaxTreeNode; import java.util.ArrayDeque; import java.util.Deque; @@ -10,7 +10,7 @@ import java.util.Deque; */ public class StackModel { - private final Deque stack; + private final Deque stack; /** * Speichert die maximale Stacktiefe während der Ausführung. @@ -21,7 +21,7 @@ public class StackModel { this.stack = new ArrayDeque<>(); } - public void push(ASTNode root) { + public void push(SyntaxTreeNode root) { this.stack.push(root); this.updateMax(); } diff --git a/src/main/java/codegen/analysis/StackSizeAnalyzer.java b/src/main/java/codegen/analysis/StackSizeAnalyzer.java index d2cb02a..3a54fc0 100644 --- a/src/main/java/codegen/analysis/StackSizeAnalyzer.java +++ b/src/main/java/codegen/analysis/StackSizeAnalyzer.java @@ -1,7 +1,7 @@ package codegen.analysis; -import parser.ast.AST; -import parser.ast.ASTNode; +import parser.ast.SyntaxTree; +import parser.ast.SyntaxTreeNode; import java.util.Set; @@ -9,7 +9,7 @@ import static util.Logger.log; /** * Ermittelt die maximal benötigte Stacktiefe für ein Programm. - * Das Programm wird übergeben als {@link AST}. + * Das Programm wird übergeben als {@link SyntaxTree}. */ public final class StackSizeAnalyzer { @@ -23,7 +23,7 @@ public final class StackSizeAnalyzer { private StackSizeAnalyzer() {} - public static int runStackModel(AST tree) { + public static int runStackModel(SyntaxTree tree) { log("\nDetermining required stack depth:"); final StackModel stack = new StackModel(); @@ -33,7 +33,7 @@ public final class StackSizeAnalyzer { return stack.getMax(); } - private static void runStackModel(ASTNode root, StackModel stack) { + private static void runStackModel(SyntaxTreeNode root, StackModel stack) { if (mod.contains(root.getName())) { switch (root.getName()) { case "assignment" -> assignment(root, stack); @@ -43,7 +43,7 @@ public final class StackSizeAnalyzer { default -> throw new IllegalStateException("Unexpected value: " + root.getName()); } } else { - for (ASTNode child : root.getChildren()) { + for (SyntaxTreeNode child : root.getChildren()) { runStackModel(child, stack); } } @@ -51,17 +51,17 @@ public final class StackSizeAnalyzer { // Simulate instructions - private static void literal(ASTNode root, StackModel stack) { + private static void literal(SyntaxTreeNode root, StackModel stack) { stack.push(root); } - private static void assignment(ASTNode root, StackModel stack) { + private static void assignment(SyntaxTreeNode root, StackModel stack) { runStackModel(root.getChildren().get(0), stack); stack.pop(); } - private static void println(ASTNode root, StackModel stack) { + private static void println(SyntaxTreeNode root, StackModel stack) { stack.push(root); // Getstatic runStackModel(root.getChildren().get(1).getChildren().get(1), stack); @@ -70,7 +70,7 @@ public final class StackSizeAnalyzer { stack.pop(); // Argument } - private static void expr(ASTNode root, StackModel stack) { + private static void expr(SyntaxTreeNode root, StackModel stack) { if (root.getChildren().size() == 2 && binaryOperators.contains(root.getValue())) { // Expression with binary operator @@ -85,7 +85,7 @@ public final class StackSizeAnalyzer { runStackModel(root.getChildren().get(0), stack); - stack.push(new ASTNode("1 (XOR)", 0)); // 1 for xor + stack.push(new SyntaxTreeNode("1 (XOR)", 0)); // 1 for xor stack.pop(); // xor stack.pop(); // xor stack.push(root); // result diff --git a/src/main/java/codegen/flowgraph/FlowGraph.java b/src/main/java/codegen/flowgraph/FlowGraph.java index e8a4b72..9e583f9 100644 --- a/src/main/java/codegen/flowgraph/FlowGraph.java +++ b/src/main/java/codegen/flowgraph/FlowGraph.java @@ -1,6 +1,6 @@ package codegen.flowgraph; -import parser.ast.AST; +import parser.ast.SyntaxTree; import util.GraphvizCaller; import util.Logger; @@ -15,7 +15,7 @@ import java.util.Optional; import java.util.stream.Collectors; /** - * Die Graph-Repräsentation des Programm, erzeugt aus einem {@link AST}. + * Die Graph-Repräsentation des Programm, erzeugt aus einem {@link SyntaxTree}. * Der Grundbaustein ist {@link FlowBasicBlock}, diese enthalten wiederum {@link FlowInstruction}. */ public class FlowGraph implements Iterable { diff --git a/src/main/java/codegen/flowgraph/FlowGraphGenerator.java b/src/main/java/codegen/flowgraph/FlowGraphGenerator.java index b270a60..48936ff 100644 --- a/src/main/java/codegen/flowgraph/FlowGraphGenerator.java +++ b/src/main/java/codegen/flowgraph/FlowGraphGenerator.java @@ -2,8 +2,8 @@ package codegen.flowgraph; import codegen.CodeGenerationException; import codegen.analysis.StackSizeAnalyzer; -import parser.ast.AST; -import parser.ast.ASTNode; +import parser.ast.SyntaxTree; +import parser.ast.SyntaxTreeNode; import typechecker.TypeChecker; import java.lang.reflect.InvocationTargetException; @@ -23,7 +23,7 @@ import static util.Logger.log; public final class FlowGraphGenerator { /** - * Mappt die {@link ASTNode}-Namen auf entsprechende Methoden für die Codeerzeugung. + * Mappt die {@link SyntaxTreeNode}-Namen auf entsprechende Methoden für die Codeerzeugung. */ private static final Map methodMap; @@ -33,16 +33,16 @@ public final class FlowGraphGenerator { try { final Class gen = FlowGraphGenerator.class; map = Map.ofEntries( - entry("cond", gen.getDeclaredMethod("condNode", ASTNode.class)), - entry("loop", gen.getDeclaredMethod("loopNode", ASTNode.class)), - entry("assignment", gen.getDeclaredMethod("assignNode", ASTNode.class)), - entry("expr", gen.getDeclaredMethod("exprNode", ASTNode.class)), + entry("cond", gen.getDeclaredMethod("condNode", SyntaxTreeNode.class)), + entry("loop", gen.getDeclaredMethod("loopNode", SyntaxTreeNode.class)), + entry("assignment", gen.getDeclaredMethod("assignNode", SyntaxTreeNode.class)), + entry("expr", gen.getDeclaredMethod("exprNode", SyntaxTreeNode.class)), // Leafs - entry("INTEGER_LIT", gen.getDeclaredMethod("intStringLiteralNode", ASTNode.class)), - entry("BOOLEAN_LIT", gen.getDeclaredMethod("boolLiteralNode", ASTNode.class)), - entry("STRING_LIT", gen.getDeclaredMethod("intStringLiteralNode", ASTNode.class)), - entry("IDENTIFIER", gen.getDeclaredMethod("identifierNode", ASTNode.class)), - entry("print", gen.getDeclaredMethod("printlnNode", ASTNode.class)) + entry("INTEGER_LIT", gen.getDeclaredMethod("intStringLiteralNode", SyntaxTreeNode.class)), + entry("BOOLEAN_LIT", gen.getDeclaredMethod("boolLiteralNode", SyntaxTreeNode.class)), + entry("STRING_LIT", gen.getDeclaredMethod("intStringLiteralNode", SyntaxTreeNode.class)), + entry("IDENTIFIER", gen.getDeclaredMethod("identifierNode", SyntaxTreeNode.class)), + entry("print", gen.getDeclaredMethod("printlnNode", SyntaxTreeNode.class)) ); } catch (NoSuchMethodException e) { map = null; @@ -51,13 +51,13 @@ public final class FlowGraphGenerator { methodMap = map; } - private final AST tree; + private final SyntaxTree tree; /** * Enthält den Rückgabetypen von jedem Expression-Node. * Wird erstellt im {@link TypeChecker}. */ - private final Map nodeTypeMap; + private final Map nodeTypeMap; /** * Enthält die Mappings vom Symbol/Variablennamen auf die Position in der JVM-Locals-Tabelle. @@ -68,7 +68,7 @@ public final class FlowGraphGenerator { private int labelCounter; - private FlowGraphGenerator(Map varMap, AST tree, Map nodeTypeMap, FlowGraph graph) { + private FlowGraphGenerator(Map varMap, SyntaxTree tree, Map nodeTypeMap, FlowGraph graph) { this.varMap = varMap; this.tree = tree; this.nodeTypeMap = nodeTypeMap; @@ -78,7 +78,7 @@ public final class FlowGraphGenerator { /** * @param source Das Source-File, welches compiliert wird (Optionaler Jasmin-Parameter) */ - public static FlowGraphGenerator fromAST(AST tree, Map nodeTypeMap, String source) { + public static FlowGraphGenerator fromAST(SyntaxTree tree, Map nodeTypeMap, String source) { if (tree.isEmpty()) { throw new CodeGenerationException("Empty File can't be compiled"); } @@ -89,17 +89,17 @@ public final class FlowGraphGenerator { return new FlowGraphGenerator(varMap, tree, nodeTypeMap, graph); } - private static Map initVarMap(AST tree) { + private static Map initVarMap(SyntaxTree tree) { final Map varMap = new HashMap<>(); - final Deque stack = new ArrayDeque<>(); + final Deque stack = new ArrayDeque<>(); stack.push(tree.getRoot()); int currentVarNumber = 0; // Assign variables to map: Symbol -> jasminLocalVarNr. while (!stack.isEmpty()) { - final ASTNode current = stack.pop(); + final SyntaxTreeNode current = stack.pop(); if ("declaration".equals(current.getName())) { // New variables only come from declarations @@ -117,7 +117,7 @@ public final class FlowGraphGenerator { return Collections.unmodifiableMap(varMap); } - private static FlowGraph initFlowGraph(AST tree, Map varMap, String source) { + private static FlowGraph initFlowGraph(SyntaxTree tree, Map varMap, String source) { final String bytecodeVersion = "49.0"; final String clazz = tree.getRoot().getChildren().get(1).getValue(); final int stackSize = StackSizeAnalyzer.runStackModel(tree); @@ -149,7 +149,7 @@ public final class FlowGraphGenerator { * Der Wurzelname wird über die methodMap einer Methode zugewiesen. * Diese wird aufgerufen und erzeugt den entsprechenden Teilbaum. */ - private void generateNode(ASTNode root) { + private void generateNode(SyntaxTreeNode root) { if (methodMap.containsKey(root.getName())) { try { methodMap.get(root.getName()).invoke(this, root); @@ -164,7 +164,7 @@ public final class FlowGraphGenerator { /** * Erzeugt den Teilbaum für einen If-Knoten. */ - private void condNode(ASTNode root) { + private void condNode(SyntaxTreeNode root) { final int currentLabel = this.labelCounter; this.labelCounter++; @@ -193,7 +193,7 @@ public final class FlowGraphGenerator { /** * Erzeugt den Teilbaum für einen While-Knoten. */ - private void loopNode(ASTNode root) { + private void loopNode(SyntaxTreeNode root) { final int currentLabel = this.labelCounter; this.labelCounter++; @@ -218,7 +218,7 @@ public final class FlowGraphGenerator { * Erzeugt den Teilbaum für Assignment-Knoten. * Die JVM-Stacksize wird dabei um 1 verringert, da istore/astore 1 Argument konsumieren. */ - private void assignNode(ASTNode root) { //! Stack - 1 + private void assignNode(SyntaxTreeNode root) { //! Stack - 1 this.generateNode(root.getChildren().get(0)); final String type = this.nodeTypeMap.get(root.getChildren().get(0)); @@ -236,7 +236,7 @@ public final class FlowGraphGenerator { /** * Wählt die entsprechende Methode für mathematische oder logische Ausdrücke. */ - private void exprNode(ASTNode root) { + private void exprNode(SyntaxTreeNode root) { if ("INTEGER_TYPE".equals(this.nodeTypeMap.get(root))) { this.intExpr(root); } else if ("BOOLEAN_TYPE".equals(this.nodeTypeMap.get(root))) { @@ -249,7 +249,7 @@ public final class FlowGraphGenerator { * Bei unären Operatoren bleibt die Stackgröße konstant (1 konsumiert, 1 Ergebnis), * bei binären Operatoren sinkt die Stackgröße um 1 (2 konsumiert, 1 Ergebnis). */ - private void intExpr(ASTNode root) { + private void intExpr(SyntaxTreeNode root) { String inst = ""; if (root.getChildren().size() == 1) { //! Stack + 0 @@ -288,7 +288,7 @@ public final class FlowGraphGenerator { * Bei unären Operatoren wächst der Stack temporär um 1 (NOT pusht eine 1 für xor), * bei binären Operatoren sinkt die Stackgröße um 1 (2 konsumiert, 1 Ergebnis). */ - private void boolExpr(ASTNode node) { + private void boolExpr(SyntaxTreeNode node) { if (node.getChildren().size() == 1) { //! Stack + 1 // Unary operator @@ -358,14 +358,14 @@ public final class FlowGraphGenerator { // Leafs - private void intStringLiteralNode(ASTNode node) { //! Stack + 1 + private void intStringLiteralNode(SyntaxTreeNode node) { //! Stack + 1 log("intStringLiteral(): " + node.getName() + ": " + node.getValue() + " => ldc"); // bipush only pushes 1 byte as int this.graph.addInstruction("ldc", node.getValue()); } - private void boolLiteralNode(ASTNode node) { //! Stack + 1 + private void boolLiteralNode(SyntaxTreeNode node) { //! Stack + 1 log("booleanLiteral(): " + node.getName() + ": " + node.getValue() + " => ldc"); final String val = "true".equals(node.getValue()) ? "1" : "0"; @@ -373,7 +373,7 @@ public final class FlowGraphGenerator { this.graph.addInstruction("ldc", val); } - private void identifierNode(ASTNode node) { //! Stack + 1 + private void identifierNode(SyntaxTreeNode node) { //! Stack + 1 final String type = this.nodeTypeMap.get(node); final String inst = switch (type) { case "INTEGER_TYPE", "BOOLEAN_TYPE" -> "iload"; @@ -386,10 +386,10 @@ public final class FlowGraphGenerator { this.graph.addInstruction(inst, this.varMap.get(node.getValue()).toString()); } - private void printlnNode(ASTNode node) { //! Stack + 1 + private void printlnNode(SyntaxTreeNode node) { //! Stack + 1 this.graph.addInstruction("getstatic", "java/lang/System/out", "Ljava/io/PrintStream;"); - final ASTNode expr = node.getChildren().get(1).getChildren().get(1); + final SyntaxTreeNode expr = node.getChildren().get(1).getChildren().get(1); final String type = switch (this.nodeTypeMap.get(expr)) { case "BOOLEAN_TYPE" -> "Z"; case "INTEGER_TYPE" -> "I"; diff --git a/src/main/java/parser/ParseException.java b/src/main/java/parser/ParseException.java index 8a37e46..fee9877 100644 --- a/src/main/java/parser/ParseException.java +++ b/src/main/java/parser/ParseException.java @@ -1,14 +1,14 @@ package parser; -import parser.ast.AST; +import parser.ast.SyntaxTree; import static util.Logger.log; public class ParseException extends RuntimeException { - public ParseException(String message, AST ast) { + public ParseException(String message, SyntaxTree syntaxTree) { super("\n" + message); - log("\nAST at last state:\n" + ast); + log("\nAST at last state:\n" + syntaxTree); } } diff --git a/src/main/java/parser/StupsParser.java b/src/main/java/parser/StupsParser.java index 633495d..e39b661 100644 --- a/src/main/java/parser/StupsParser.java +++ b/src/main/java/parser/StupsParser.java @@ -2,8 +2,8 @@ package parser; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.Vocabulary; -import parser.ast.AST; -import parser.ast.ASTNode; +import parser.ast.SyntaxTree; +import parser.ast.SyntaxTreeNode; import parser.grammar.Grammar; import parser.grammar.GrammarAnalyzer; @@ -37,11 +37,11 @@ public class StupsParser { srcLine.ifPresent(s -> System.out.println(" :: " + s)); } - public AST parse(List token, Vocabulary voc) { + public SyntaxTree parse(List token, Vocabulary voc) { System.out.println(" - Parsing program..."); - final ASTNode root = new ASTNode(this.parsetable.getStartSymbol(), 0); - final AST tree = new AST(root); - final Deque stack = new ArrayDeque<>(); + final SyntaxTreeNode root = new SyntaxTreeNode(this.parsetable.getStartSymbol(), 0); + final SyntaxTree tree = new SyntaxTree(root); + final Deque stack = new ArrayDeque<>(); stack.push(root); int inputPosition = 0; @@ -97,12 +97,12 @@ public class StupsParser { // Hier wird auch der AST aufgebaut log("Used: " + top + " -> " + prod); - final ASTNode pop = stack.pop(); + final SyntaxTreeNode pop = stack.pop(); final String[] split = prod.split(" "); for (int i = split.length - 1; i >= 0; i--) { - final ASTNode node = new ASTNode(split[i], currentLine); + final SyntaxTreeNode node = new SyntaxTreeNode(split[i], currentLine); if (inputPosition + i < token.size()) { // Die Schleife geht in der Eingabe weiter diff --git a/src/main/java/parser/ast/AST.java b/src/main/java/parser/ast/AST.java deleted file mode 100644 index 2a453fa..0000000 --- a/src/main/java/parser/ast/AST.java +++ /dev/null @@ -1,57 +0,0 @@ -package parser.ast; - -import parser.grammar.Grammar; - -import java.util.Objects; - -public class AST { - - private final ASTNode root; - - public AST(ASTNode root) { - this.root = root; - } - - public ASTNode getRoot() { - return this.root; - } - - public long size() { - return this.root.size(); - } - - public boolean isEmpty() { - return this.root.isEmpty(); - } - - public void postprocess(Grammar grammar) { - ASTCompacter.clean(this, grammar); - ASTBalancer.balance(this); - System.out.println("Tree processing successful."); - } - - public AST deepCopy() { - return new AST(this.root.deepCopy()); - } - - // Overrides - - @Override - public boolean equals(Object obj) { - if (obj instanceof AST) { - return this.root.equals(((AST) obj).root); - } - - return false; - } - - @Override - public String toString() { - return this.root.toString(); - } - - @Override - public int hashCode() { - return Objects.hash(this.root); - } -} diff --git a/src/main/java/parser/ast/ASTBalancer.java b/src/main/java/parser/ast/ASTBalancer.java index 1c358d3..4593a0b 100644 --- a/src/main/java/parser/ast/ASTBalancer.java +++ b/src/main/java/parser/ast/ASTBalancer.java @@ -43,7 +43,7 @@ public final class ASTBalancer { private ASTBalancer() {} - public static void balance(AST tree) { + public static void balance(SyntaxTree tree) { flip(tree); leftPrecedence(tree); operatorPrecedence(tree); @@ -56,26 +56,26 @@ public final class ASTBalancer { } // Baum spiegeln, damit höhere Ebenen links sind und EXPR vorwärts laufen - public static void flip(AST tree) { + public static void flip(SyntaxTree tree) { log("Flipping tree for ltr evaluation"); flip(tree.getRoot()); } - private static void flip(ASTNode root) { - for (ASTNode child : root.getChildren()) { + private static void flip(SyntaxTreeNode root) { + for (SyntaxTreeNode child : root.getChildren()) { flip(child); } Collections.reverse(root.getChildren()); } - public static void flipCommutativeExpr(AST tree) { + public static void flipCommutativeExpr(SyntaxTree tree) { log("Flipping commutative expressions for stack efficiency"); flipCommutativeExpr(tree.getRoot()); } - private static void flipCommutativeExpr(ASTNode root) { - for (ASTNode child : root.getChildren()) { + private static void flipCommutativeExpr(SyntaxTreeNode root) { + for (SyntaxTreeNode child : root.getChildren()) { flipCommutativeExpr(child); } @@ -92,18 +92,18 @@ public final class ASTBalancer { // Führt Linksrotationen durch // Es werden EXPR-Nodes (2 Childs, 1 davon EXPR, Kein Wert) solange wie möglich linksrotiert - public static void leftPrecedence(AST tree) { + public static void leftPrecedence(SyntaxTree tree) { log("Left-rotating expressions for left-precedence"); leftPrecedence(tree.getRoot()); } // Es wird solange rotiert bis die letzte "Rotation" durchgeführt wurde - private static void leftPrecedence(ASTNode root) { - for (ASTNode child : root.getChildren()) { + private static void leftPrecedence(SyntaxTreeNode root) { + for (SyntaxTreeNode child : root.getChildren()) { leftPrecedence(child); } - final ASTNode expr = getExpr(root); + final SyntaxTreeNode expr = getExpr(root); if (expr == null || root.getChildren().size() != 2 || !root.getValue().isEmpty()) { return; @@ -117,12 +117,12 @@ public final class ASTBalancer { } // Die Letzte Rotation ist keine richtige Rotation, dort wird false zurückgegeben - private static boolean specialLeftRotate(ASTNode root) { + private static boolean specialLeftRotate(SyntaxTreeNode root) { log("Special-Left-Rotation around " + root.getName()); log(root.toString()); - final ASTNode left = root.getChildren().get(0); - final ASTNode right = root.getChildren().get(1); + final SyntaxTreeNode left = root.getChildren().get(0); + final SyntaxTreeNode right = root.getChildren().get(1); // Verhindert Wurzel mit nur einem EXPR-Child (nach oben "hängende" Wurzel) if (endOfExpr(right)) { @@ -132,7 +132,7 @@ public final class ASTBalancer { return false; // Braucht keine weitere Rotation } - final ASTNode insertLeft = new ASTNode(root.getName(), root.getLine()); + final SyntaxTreeNode insertLeft = new SyntaxTreeNode(root.getName(), root.getLine()); insertLeft.setValue(right.getValue()); // Operation wird linksvererbt insertLeft.setChildren(left, right.getChildren().get(0)); @@ -143,8 +143,8 @@ public final class ASTBalancer { } // Findet die 1te (linkeste) expr - private static ASTNode getExpr(ASTNode root) { - for (ASTNode child : root.getChildren()) { + private static SyntaxTreeNode getExpr(SyntaxTreeNode root) { + for (SyntaxTreeNode child : root.getChildren()) { if ("expr".equals(child.getName())) { return child; } @@ -153,11 +153,11 @@ public final class ASTBalancer { return null; } - private static boolean endOfExpr(ASTNode root) { + private static boolean endOfExpr(SyntaxTreeNode root) { return root.getChildren().size() == 1; } - public static void operatorPrecedence(AST tree) { + public static void operatorPrecedence(SyntaxTree tree) { log("Right-rotating expressions for operator-precedence"); boolean changed; @@ -167,10 +167,10 @@ public final class ASTBalancer { } while (changed); } - public static boolean operatorPrecedence(ASTNode root) { + public static boolean operatorPrecedence(SyntaxTreeNode root) { boolean changed = false; - for (ASTNode child : root.getChildren()) { + for (SyntaxTreeNode child : root.getChildren()) { changed = changed || operatorPrecedence(child); if (preceding(root, child)) { @@ -182,7 +182,7 @@ public final class ASTBalancer { return changed; } - private static boolean preceding(ASTNode parent, ASTNode child) { + private static boolean preceding(SyntaxTreeNode parent, SyntaxTreeNode child) { if (!"expr".equals(parent.getName()) || parent.getValue().isEmpty() || !"expr".equals(child.getName()) || child.getValue().isEmpty()) { return false; @@ -199,14 +199,14 @@ public final class ASTBalancer { } } - private static void simpleRightRotate(ASTNode root) { + private static void simpleRightRotate(SyntaxTreeNode root) { log("Right-Rotation around " + root.getName() + ": " + root.getValue()); log(root.toString()); - final ASTNode left = root.getChildren().get(0); - final ASTNode right = root.getChildren().get(1); + final SyntaxTreeNode left = root.getChildren().get(0); + final SyntaxTreeNode right = root.getChildren().get(1); - final ASTNode insertRight = new ASTNode(root.getName(), root.getLine()); + final SyntaxTreeNode insertRight = new SyntaxTreeNode(root.getName(), root.getLine()); insertRight.setValue(root.getValue()); insertRight.setChildren(left.getChildren().get(1), right); diff --git a/src/main/java/parser/ast/ASTCompacter.java b/src/main/java/parser/ast/ASTCompacter.java index 2e5daa5..417f27a 100644 --- a/src/main/java/parser/ast/ASTCompacter.java +++ b/src/main/java/parser/ast/ASTCompacter.java @@ -11,7 +11,7 @@ public final class ASTCompacter { private ASTCompacter() {} - public static void clean(AST tree, Grammar grammar) { + public static void clean(SyntaxTree tree, Grammar grammar) { deleteChildren(tree, grammar); deleteIfEmpty(tree, grammar); promote(tree, grammar); @@ -26,15 +26,15 @@ public final class ASTCompacter { } // Entfernt [promote]-able Nodes (Reicht Werte nach oben) - public static void promote(AST tree, Grammar grammar) { + public static void promote(SyntaxTree tree, Grammar grammar) { log("\nPromoting nodes:"); promote(tree.getRoot(), grammar); } - private static void promote(ASTNode root, Grammar grammar) { - final Collection toRemove = new HashSet<>(); + private static void promote(SyntaxTreeNode root, Grammar grammar) { + final Collection toRemove = new HashSet<>(); - for (ASTNode child : root.getChildren()) { + for (SyntaxTreeNode child : root.getChildren()) { promote(child, grammar); // Impliziert, dass die for-schleife nur 1x läuft, deshalb ist child das richtige Kind @@ -57,15 +57,15 @@ public final class ASTCompacter { } // Entfernt [delIfEmpty] Nodes (löscht Nodes ohne Inhalt) - public static void deleteIfEmpty(AST tree, Grammar grammar) { + public static void deleteIfEmpty(SyntaxTree tree, Grammar grammar) { log("\nDeleting empty nodes:"); deleteIfEmpty(tree.getRoot(), grammar); } - private static void deleteIfEmpty(ASTNode root, Grammar grammar) { - final Collection toRemove = new HashSet<>(); + private static void deleteIfEmpty(SyntaxTreeNode root, Grammar grammar) { + final Collection toRemove = new HashSet<>(); - for (ASTNode child : root.getChildren()) { + for (SyntaxTreeNode child : root.getChildren()) { deleteIfEmpty(child, grammar); if (!grammar.canDeleteIfEmpty(child)) { @@ -82,15 +82,15 @@ public final class ASTCompacter { } // Löscht redundante Informationen in [delChildren]-Nodes (z.b. IF-child von COND) und Epsilon-Nodes - public static void deleteChildren(AST tree, Grammar grammar) { + public static void deleteChildren(SyntaxTree tree, Grammar grammar) { log("Removing redundant children:"); deleteChildren(tree.getRoot(), grammar); } - private static void deleteChildren(ASTNode root, Grammar grammar) { - final Collection toRemove = new HashSet<>(); + private static void deleteChildren(SyntaxTreeNode root, Grammar grammar) { + final Collection toRemove = new HashSet<>(); - for (ASTNode child : root.getChildren()) { + for (SyntaxTreeNode child : root.getChildren()) { deleteChildren(child, grammar); if (!grammar.canDeleteChild(root, child)) { @@ -107,13 +107,13 @@ public final class ASTCompacter { } // Umbenennungen - private static void renameTo(AST tree, Grammar grammar) { + private static void renameTo(SyntaxTree tree, Grammar grammar) { log("\nRenaming nodes:"); renameTo(tree.getRoot(), grammar); } - private static void renameTo(ASTNode root, Grammar grammar) { - for (ASTNode child : root.getChildren()) { + private static void renameTo(SyntaxTreeNode root, Grammar grammar) { + for (SyntaxTreeNode child : root.getChildren()) { renameTo(child, grammar); if (!grammar.canBeRenamed(root)) { @@ -126,15 +126,15 @@ public final class ASTCompacter { } } - public static void nameToValue(AST tree, Grammar grammar) { + public static void nameToValue(SyntaxTree tree, Grammar grammar) { log("\nMoving names to values:"); nameToValue(tree.getRoot(), grammar); } - private static void nameToValue(ASTNode root, Grammar grammar) { - final Collection toRemove = new HashSet<>(); + private static void nameToValue(SyntaxTreeNode root, Grammar grammar) { + final Collection toRemove = new HashSet<>(); - for (ASTNode child : root.getChildren()) { + for (SyntaxTreeNode child : root.getChildren()) { nameToValue(child, grammar); if (!grammar.canMoveNameToVal(root, child)) { @@ -154,15 +154,15 @@ public final class ASTCompacter { } // Assignment bekommt den Identifier als Value anstatt als Child - public static void valueToValue(AST tree, Grammar grammar) { + public static void valueToValue(SyntaxTree tree, Grammar grammar) { log("\nMoving values to values:"); valueToValue(tree.getRoot(), grammar); } - private static void valueToValue(ASTNode root, Grammar grammar) { - final Collection toRemove = new HashSet<>(); + private static void valueToValue(SyntaxTreeNode root, Grammar grammar) { + final Collection toRemove = new HashSet<>(); - for (ASTNode child : root.getChildren()) { + for (SyntaxTreeNode child : root.getChildren()) { valueToValue(child, grammar); if (!grammar.hasValToVal(root, child)) { diff --git a/src/main/java/parser/ast/ASTNode.java b/src/main/java/parser/ast/ASTNode.java deleted file mode 100644 index 12b600c..0000000 --- a/src/main/java/parser/ast/ASTNode.java +++ /dev/null @@ -1,126 +0,0 @@ -package parser.ast; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.List; -import java.util.Objects; -import java.util.stream.Collectors; - -public class ASTNode { - - private final int line; - private String name; - private String value; - private List children = new ArrayList<>(); - - public ASTNode(String name, int line) { - this.name = name; - this.line = line; - this.value = ""; - } - - public void addChild(ASTNode ASTNode) { - this.children.add(ASTNode); - } - - public boolean hasChildren() { - return !this.children.isEmpty(); - } - - public List getChildren() { - return this.children; - } - - public void setChildren(List children) { - this.children = children; - } - - public void setChildren(ASTNode... children) { - this.children = new ArrayList<>(); - this.children.addAll(Arrays.asList(children)); - } - - public String getName() { - return this.name; - } - - public void setName(String name) { - this.name = name; - } - - @Override - public int hashCode() { - return Objects.hash(this.name, this.value, this.children, this.line); - } - - @Override - public boolean equals(Object obj) { - if (obj instanceof ASTNode) { - return this.name.equals(((ASTNode) obj).name) - && this.value.equals(((ASTNode) obj).value) - && this.children.equals(((ASTNode) obj).children) - && this.line == ((ASTNode) obj).line; - } - - return false; - } - - // toString() und print() von hier: https://stackoverflow.com/a/8948691 - @Override - public String toString() { - final StringBuilder buffer = new StringBuilder(50); - this.print(buffer, "", ""); - return buffer.toString(); - } - - public String getValue() { - return this.value; - } - - public void setValue(String value) { - this.value = value; - } - - private void print(StringBuilder buffer, String prefix, String childrenPrefix) { - buffer.append(prefix); - buffer.append(this.name); - if (!this.value.isBlank()) { - buffer.append(": "); - buffer.append(this.value); - } - buffer.append('\n'); - - for (final Iterator it = this.children.listIterator(); it.hasNext(); ) { - final ASTNode next = it.next(); - if (it.hasNext()) { - next.print(buffer, childrenPrefix + "├── ", childrenPrefix + "│ "); - } else { - next.print(buffer, childrenPrefix + "└── ", childrenPrefix + " "); - } - } - } - - public long size() { - return 1 + this.children.stream().mapToLong(ASTNode::size).sum(); - } - - public int getLine() { - return this.line; - } - - public ASTNode deepCopy() { - final ASTNode newNode = new ASTNode(this.name, this.line); - - newNode.value = this.value; - newNode.children = this.children.stream() - .map(ASTNode::deepCopy) - .collect(Collectors.toList()); - - return newNode; - } - - public boolean isEmpty() { - return this.children.isEmpty(); - } -} diff --git a/src/main/java/parser/ast/SyntaxTree.java b/src/main/java/parser/ast/SyntaxTree.java new file mode 100644 index 0000000..465c884 --- /dev/null +++ b/src/main/java/parser/ast/SyntaxTree.java @@ -0,0 +1,74 @@ +package parser.ast; + +import parser.grammar.Grammar; + +import java.util.Objects; + +/** + * Repräsentiert konkrete und abstrakte Parse-/Syntaxbäume. + */ +public class SyntaxTree { + + private final SyntaxTreeNode root; + + public SyntaxTree(SyntaxTreeNode root) { + this.root = root; + } + + /** + * Formt einen konkreten Parsebaum in einein abstrakten SyntaxTree um. + * + * @param grammar Die Parsegrammatik wird benötigt um die Umformungen durchzuführen. + */ + public static SyntaxTree toAbstractSyntaxTree(SyntaxTree concreteSyntaxTree, Grammar grammar) { + final SyntaxTree abstractSyntaxTree = concreteSyntaxTree.deepCopy(); + + ASTCompacter.clean(abstractSyntaxTree, grammar); + ASTBalancer.balance(abstractSyntaxTree); + System.out.println("Tree processing successful."); + + return abstractSyntaxTree; + } + + public SyntaxTree deepCopy() { + return new SyntaxTree(this.root.deepCopy()); + } + + // Getters + + public SyntaxTreeNode getRoot() { + return this.root; + } + + public long size() { + return this.root.size(); + } + + public boolean isEmpty() { + return this.root.isEmpty(); + } + + // Overrides + + @Override + public int hashCode() { + return Objects.hash(this.root); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + final SyntaxTree that = (SyntaxTree) o; + return this.root.equals(that.root); + } + + @Override + public String toString() { + return this.root.toString(); + } +} diff --git a/src/main/java/parser/ast/SyntaxTreeNode.java b/src/main/java/parser/ast/SyntaxTreeNode.java new file mode 100644 index 0000000..5453086 --- /dev/null +++ b/src/main/java/parser/ast/SyntaxTreeNode.java @@ -0,0 +1,136 @@ +package parser.ast; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * Repräsentiert einen Token aus dem Quellprogramm im Parsebaum, + * oder eine abstrakte Anweisung im Abstrakten Syntaxbaum. + */ +public class SyntaxTreeNode { + + private final int line; + private String name; + private String value; + private List children = new ArrayList<>(); + + public SyntaxTreeNode(String name, int line) { + this.name = name; + this.line = line; + this.value = ""; + } + + public SyntaxTreeNode deepCopy() { + final SyntaxTreeNode newNode = new SyntaxTreeNode(this.name, this.line); + + newNode.value = this.value; + newNode.children = this.children.stream() + .map(SyntaxTreeNode::deepCopy) + .collect(Collectors.toList()); + + return newNode; + } + + // Getters, Setters + + public boolean isEmpty() { + return this.children.isEmpty(); + } + + public long size() { + return 1 + this.children.stream().mapToLong(SyntaxTreeNode::size).sum(); + } + + public String getValue() { + return this.value; + } + + public void setValue(String value) { + this.value = value; + } + + public int getLine() { + return this.line; + } + + public List getChildren() { + return this.children; + } + + public void setChildren(List children) { + this.children = children; + } + + public void setChildren(SyntaxTreeNode... children) { + this.children = new ArrayList<>(); + this.children.addAll(Arrays.asList(children)); + } + + public void addChild(SyntaxTreeNode syntaxTreeNode) { + this.children.add(syntaxTreeNode); + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + // Printing + + // toString() und treePrint() von hier: https://stackoverflow.com/a/8948691 + private void treePrint(StringBuilder buffer, String prefix, String childrenPrefix) { + buffer.append(prefix); + buffer.append(this.name); + if (!this.value.isBlank()) { + buffer.append(": "); + buffer.append(this.value); + } + buffer.append('\n'); + + for (final Iterator it = this.children.listIterator(); it.hasNext(); ) { + final SyntaxTreeNode next = it.next(); + if (it.hasNext()) { + next.treePrint(buffer, childrenPrefix + "├── ", childrenPrefix + "│ "); + } else { + next.treePrint(buffer, childrenPrefix + "└── ", childrenPrefix + " "); + } + } + } + + // Overrides + + @Override + @SuppressWarnings("NonFinalFieldReferencedInHashCode") + public int hashCode() { + return Objects.hash(this.line, this.name, this.value, this.children); + } + + @Override + @SuppressWarnings("NonFinalFieldReferenceInEquals") + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || this.getClass() != o.getClass()) { + return false; + } + final SyntaxTreeNode that = (SyntaxTreeNode) o; + return this.line == that.line && this.name.equals(that.name) + && this.value.equals(that.value) && this.children.equals(that.children); + } + + // toString() und treePrint() von hier: https://stackoverflow.com/a/8948691 + @Override + public String toString() { + final StringBuilder buffer = new StringBuilder(); + this.treePrint(buffer, "", ""); + return buffer.toString(); + } +} diff --git a/src/main/java/parser/grammar/Grammar.java b/src/main/java/parser/grammar/Grammar.java index df69675..11662fa 100644 --- a/src/main/java/parser/grammar/Grammar.java +++ b/src/main/java/parser/grammar/Grammar.java @@ -1,6 +1,6 @@ package parser.grammar; -import parser.ast.ASTNode; +import parser.ast.SyntaxTreeNode; import java.io.IOException; import java.nio.file.Files; @@ -243,7 +243,7 @@ public class Grammar { * Es wird nicht root promoted, sondern roots einziges Kind. * Checkt auch auf Anzahl der Kinder. */ - public boolean canPromoteChild(ASTNode root) { + public boolean canPromoteChild(SyntaxTreeNode root) { return this.canPromoteChild(root.getName()) && root.getChildren().size() == 1 && root.getValue().isEmpty(); @@ -257,10 +257,10 @@ public class Grammar { /** * Checkt auch auf Anzahl der Kinder und vorhandene Value. */ - public boolean canDeleteIfEmpty(ASTNode root) { + public boolean canDeleteIfEmpty(SyntaxTreeNode root) { return this.canDeleteIfEmpty(root.getName()) && root.getValue().isEmpty() - && !root.hasChildren(); + && root.isEmpty(); } public boolean canDeleteIfEmpty(String sym) { @@ -272,9 +272,9 @@ public class Grammar { * Checkt auch auf Anzahl der Kinder. * Epsilon-Knoten werden immer gelöscht. */ - public boolean canDeleteChild(ASTNode parent, ASTNode child) { + public boolean canDeleteChild(SyntaxTreeNode parent, SyntaxTreeNode child) { return this.canDeleteChild(parent.getName(), child.getName()) - && !child.hasChildren(); + && child.isEmpty(); } public boolean canDeleteChild(String parent, String child) { @@ -284,7 +284,7 @@ public class Grammar { } - public boolean canBeRenamed(ASTNode root) { + public boolean canBeRenamed(SyntaxTreeNode root) { return this.canBeRenamed(root.getName()); } @@ -292,7 +292,7 @@ public class Grammar { return this.actions.get(RENAMETO).contains(sym); } - public String getNewName(ASTNode root) { + public String getNewName(SyntaxTreeNode root) { return this.getNewName(root.getName()); } @@ -301,7 +301,7 @@ public class Grammar { } - public boolean hasValToVal(ASTNode parent, ASTNode child) { + public boolean hasValToVal(SyntaxTreeNode parent, SyntaxTreeNode child) { return this.hasValToVal(parent.getName(), child.getName()); } @@ -314,7 +314,7 @@ public class Grammar { /** * Checkt auch auf bereits existierende Values. */ - public boolean canMoveNameToVal(ASTNode parent, ASTNode child) { + public boolean canMoveNameToVal(SyntaxTreeNode parent, SyntaxTreeNode child) { return this.canMoveNameToVal(parent.getName(), child.getName()) && parent.getValue().isEmpty(); } diff --git a/src/main/java/typechecker/TypeChecker.java b/src/main/java/typechecker/TypeChecker.java index 3ed6d66..214aeff 100644 --- a/src/main/java/typechecker/TypeChecker.java +++ b/src/main/java/typechecker/TypeChecker.java @@ -1,7 +1,7 @@ package typechecker; -import parser.ast.AST; -import parser.ast.ASTNode; +import parser.ast.SyntaxTree; +import parser.ast.SyntaxTreeNode; import java.util.Arrays; import java.util.Collection; @@ -20,9 +20,9 @@ public final class TypeChecker { // TODO: nodeTable? // Wirft exception bei typeerror, return nodeTable? - public static Map validate(AST tree) { + public static Map validate(SyntaxTree tree) { final TypeTable table = TypeTable.fromAST(tree); - final Map nodeTable = new HashMap<>(); + final Map nodeTable = new HashMap<>(); System.out.println(" - Validating syntax-tree..."); @@ -34,8 +34,8 @@ public final class TypeChecker { return nodeTable; } - private static void validate(ASTNode root, TypeTable table, Map nodeTable) { - for (ASTNode child : root.getChildren()) { + private static void validate(SyntaxTreeNode root, TypeTable table, Map nodeTable) { + for (SyntaxTreeNode child : root.getChildren()) { validate(child, table, nodeTable); } @@ -55,7 +55,7 @@ public final class TypeChecker { } else if ("par_expr".equals(root.getName())) { // Nodetable Eintrag für Klammern - final ASTNode centerChild = root.getChildren().get(1); + final SyntaxTreeNode centerChild = root.getChildren().get(1); nodeTable.put(root, nodeTable.get(centerChild)); } else if ("IDENTIFIER".equals(root.getName())) { @@ -73,10 +73,10 @@ public final class TypeChecker { } } - private static void validateAssignment(ASTNode root, TypeTable table, Map nodeTable) { + private static void validateAssignment(SyntaxTreeNode root, TypeTable table, Map nodeTable) { final String identifier = root.getValue(); final String identifierType = table.getSymbolType(identifier); - final ASTNode literalNode = root.getChildren().get(0); + final SyntaxTreeNode literalNode = root.getChildren().get(0); final String literalType = nodeTable.get(literalNode); log("Validating Assignment: " + identifierType + ": " + identifier + " = " + literalType); @@ -88,12 +88,12 @@ public final class TypeChecker { } } - private static void validateExpression(ASTNode root, TypeTable table, Map nodeTable) { + private static void validateExpression(SyntaxTreeNode root, TypeTable table, Map nodeTable) { final String op = root.getValue(); log("Validating Expression: " + root.getValue()); - if (!root.hasChildren()) { + if (root.isEmpty()) { // Keine Kinder System.out.println("Line " + root.getLine() + " Operatorerror: Can't use [" + op + "] without arguments"); @@ -115,7 +115,7 @@ public final class TypeChecker { } final List requiredType = table.getMethodArgumentType(op); - for (ASTNode child : root.getChildren()) { + for (SyntaxTreeNode child : root.getChildren()) { // Jedes Child muss korrekten Typ zurückgeben final String childReturnType = nodeTable.get(child); @@ -137,8 +137,8 @@ public final class TypeChecker { } if ("EQUAL".equals(op) || "NOT_EQUAL".equals(op)) { - final ASTNode left = root.getChildren().get(0); - final ASTNode right = root.getChildren().get(1); + final SyntaxTreeNode left = root.getChildren().get(0); + 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) + "]"); diff --git a/src/main/java/typechecker/TypeTable.java b/src/main/java/typechecker/TypeTable.java index 70810e9..d0b35e3 100644 --- a/src/main/java/typechecker/TypeTable.java +++ b/src/main/java/typechecker/TypeTable.java @@ -1,7 +1,7 @@ package typechecker; -import parser.ast.AST; -import parser.ast.ASTNode; +import parser.ast.SyntaxTree; +import parser.ast.SyntaxTreeNode; import java.util.Arrays; import java.util.Collections; @@ -53,7 +53,7 @@ public class TypeTable { Map.entry("NOT_EQUAL", Arrays.asList("INTEGER_TYPE", "BOOLEAN_TYPE", "STRING_TYPE"))); } - public static TypeTable fromAST(AST tree) { + public static TypeTable fromAST(SyntaxTree tree) { System.out.println(" - Building TypeTable..."); final Map tableOut = new HashMap<>(); @@ -64,13 +64,13 @@ public class TypeTable { return new TypeTable(tableOut); } - private static void scanTree(ASTNode root, Map table) { - for (ASTNode child : root.getChildren()) { + private static void scanTree(SyntaxTreeNode root, Map table) { + for (SyntaxTreeNode child : root.getChildren()) { scanTree(child, table); } if ("declaration".equals(root.getName())) { - final ASTNode child = root.getChildren().get(0); + final SyntaxTreeNode child = root.getChildren().get(0); log("Adding Entry " + child.getValue() + " -> " + root.getValue()); final String oldEntry = table.put(child.getValue(), root.getValue()); diff --git a/src/test/java/codegen/FlowGraphGeneratorTest.java b/src/test/java/codegen/FlowGraphGeneratorTest.java index 8a7462e..26dc74f 100644 --- a/src/test/java/codegen/FlowGraphGeneratorTest.java +++ b/src/test/java/codegen/FlowGraphGeneratorTest.java @@ -11,8 +11,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import parser.StupsParser; -import parser.ast.AST; -import parser.ast.ASTNode; +import parser.ast.SyntaxTree; +import parser.ast.SyntaxTreeNode; import parser.grammar.Grammar; import typechecker.TypeChecker; @@ -53,13 +53,13 @@ class FlowGraphGeneratorTest { return null; } - private static AST lexParseProgram(String prog) { + private static SyntaxTree lexParseProgram(String prog) { final Lexer lex = new StupsLexer(CharStreams.fromString(prog)); - final AST tree = parser.parse(lex.getAllTokens(), lex.getVocabulary()); - tree.postprocess(stupsGrammar); + final SyntaxTree tree = parser.parse(lex.getAllTokens(), lex.getVocabulary()); + final SyntaxTree ast = SyntaxTree.toAbstractSyntaxTree(tree, stupsGrammar); - return tree; + return ast; } private static void codegenToFile(String src) { @@ -218,8 +218,8 @@ class FlowGraphGeneratorTest { final String program = buildArithmeticProg(prog); System.out.println(program); - final AST tree = lexParseProgram(program); - final Map nodeTable = TypeChecker.validate(tree); + final SyntaxTree tree = lexParseProgram(program); + final Map nodeTable = TypeChecker.validate(tree); final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); final FlowGraph srcProg = gen.generateGraph(); @@ -235,8 +235,8 @@ class FlowGraphGeneratorTest { final String program = buildIfElseProgram(expr, condition, ifBlock, elseBlock); System.out.println(program); - final AST tree = lexParseProgram(program); - final Map nodeTable = TypeChecker.validate(tree); + final SyntaxTree tree = lexParseProgram(program); + final Map nodeTable = TypeChecker.validate(tree); final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); final FlowGraph srcProg = gen.generateGraph(); @@ -278,8 +278,8 @@ class FlowGraphGeneratorTest { final String program = buildLogicProgram(expr); System.out.println(program); - final AST tree = lexParseProgram(program); - final Map nodeTable = TypeChecker.validate(tree); + final SyntaxTree tree = lexParseProgram(program); + final Map nodeTable = TypeChecker.validate(tree); final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); final FlowGraph srcProg = gen.generateGraph(); @@ -295,8 +295,8 @@ class FlowGraphGeneratorTest { final String program = buildLoopProgram(expr, condition, body); System.out.println(program); - final AST tree = lexParseProgram(program); - final Map nodeTable = TypeChecker.validate(tree); + final SyntaxTree tree = lexParseProgram(program); + final Map nodeTable = TypeChecker.validate(tree); final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); final FlowGraph srcProg = gen.generateGraph(); @@ -310,8 +310,8 @@ class FlowGraphGeneratorTest { final String program = readProgram(prog); System.out.print(program); - final AST tree = lexParseProgram(program); - final Map nodeTable = TypeChecker.validate(tree); + final SyntaxTree tree = lexParseProgram(program); + final Map nodeTable = TypeChecker.validate(tree); final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); final FlowGraph srcProg = gen.generateGraph(); @@ -323,8 +323,8 @@ class FlowGraphGeneratorTest { void compileEmptyProgramTest() { final String program = readProgram("EmptyFile.stups"); - final AST tree = lexParseProgram(program); - final Map nodeTable = TypeChecker.validate(tree); + final SyntaxTree tree = lexParseProgram(program); + final Map nodeTable = TypeChecker.validate(tree); assertThatThrownBy(() -> FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput")) .isInstanceOf(CodeGenerationException.class); diff --git a/src/test/java/codegen/analysis/liveness/LivenessAnalysisTest.java b/src/test/java/codegen/analysis/liveness/LivenessAnalysisTest.java index 3d1fcfb..04d1a57 100644 --- a/src/test/java/codegen/analysis/liveness/LivenessAnalysisTest.java +++ b/src/test/java/codegen/analysis/liveness/LivenessAnalysisTest.java @@ -11,8 +11,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import parser.StupsParser; -import parser.ast.AST; -import parser.ast.ASTNode; +import parser.ast.SyntaxTree; +import parser.ast.SyntaxTreeNode; import parser.grammar.Grammar; import typechecker.TypeChecker; @@ -40,18 +40,18 @@ class LivenessAnalysisTest { stupsGrammar = grammar; } - private static AST lexParseProgram(String prog) { + private static SyntaxTree lexParseProgram(String prog) { final Lexer lex = new StupsLexer(CharStreams.fromString(prog)); - final AST tree = parser.parse(lex.getAllTokens(), lex.getVocabulary()); - tree.postprocess(stupsGrammar); + final SyntaxTree tree = parser.parse(lex.getAllTokens(), lex.getVocabulary()); + final SyntaxTree ast = SyntaxTree.toAbstractSyntaxTree(tree, stupsGrammar); - return tree; + return ast; } private static LivenessAnalysis initLivenessAnalysis(String program) { - final AST tree = lexParseProgram(program); - final Map nodeTable = TypeChecker.validate(tree); + final SyntaxTree tree = lexParseProgram(program); + final Map nodeTable = TypeChecker.validate(tree); final FlowGraphGenerator gen = FlowGraphGenerator.fromAST(tree, nodeTable, "TestOutput"); final FlowGraph graph = gen.generateGraph(); final DataFlowGraph dataGraph = DataFlowGraph.fromFlowGraph(graph); diff --git a/src/test/java/parser/ast/ASTTest.java b/src/test/java/parser/ast/ASTTest.java deleted file mode 100644 index 57dfc1d..0000000 --- a/src/test/java/parser/ast/ASTTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package parser.ast; - -import org.junit.jupiter.api.Test; - -import static org.assertj.core.api.Assertions.assertThat; - -class ASTTest { - - @Test - void testOneNode() { - final ASTNode root = new ASTNode("Wurzel", 1); - - final AST tree = new AST(root); - - assertThat(tree).hasToString("Wurzel\n"); - } - - @Test - void testThreeNodesBinary() { - final ASTNode root = new ASTNode("Wurzel", 1); - final ASTNode childA = new ASTNode("A", 1); - final ASTNode childB = new ASTNode("B", 1); - - root.addChild(childA); - root.addChild(childB); - - final AST tree = new AST(root); - - assertThat(tree).hasToString("Wurzel\n├── A\n└── B\n"); - } - - @Test - void testThreeNodesLinear() { - final ASTNode root = new ASTNode("Wurzel", 1); - final ASTNode childA = new ASTNode("A", 1); - final ASTNode childB = new ASTNode("B", 1); - - root.addChild(childA); - childA.addChild(childB); - - final AST tree = new AST(root); - - assertThat(tree).hasToString("Wurzel\n└── A\n └── B\n"); - } -} diff --git a/src/test/java/parser/ast/ASTBalancerTest.java b/src/test/java/parser/ast/SyntaxTreeBalancerTest.java similarity index 65% rename from src/test/java/parser/ast/ASTBalancerTest.java rename to src/test/java/parser/ast/SyntaxTreeBalancerTest.java index 052fe4a..7fe41bf 100644 --- a/src/test/java/parser/ast/ASTBalancerTest.java +++ b/src/test/java/parser/ast/SyntaxTreeBalancerTest.java @@ -4,22 +4,22 @@ import org.junit.jupiter.api.Test; import static org.assertj.core.api.Assertions.assertThat; -class ASTBalancerTest { +class SyntaxTreeBalancerTest { //expr //├── expr: SUB //| └── INTEGER_LIT: 2 //└── INTEGER_LIT: 1 - private static AST tree1() { - final AST tree = new AST(new ASTNode("epxr", 1)); + private static SyntaxTree tree1() { + final SyntaxTree tree = new SyntaxTree(new SyntaxTreeNode("epxr", 1)); - final ASTNode right = new ASTNode("INTEGER_LIT", 1); + final SyntaxTreeNode right = new SyntaxTreeNode("INTEGER_LIT", 1); right.setValue("1"); - final ASTNode left = new ASTNode("expr", 1); + final SyntaxTreeNode left = new SyntaxTreeNode("expr", 1); left.setValue("SUB"); - final ASTNode lleft = new ASTNode("INTEGER_LIT", 1); + final SyntaxTreeNode lleft = new SyntaxTreeNode("INTEGER_LIT", 1); lleft.setValue("2"); left.setChildren(lleft); @@ -28,22 +28,22 @@ class ASTBalancerTest { return tree; } - private static AST tree2() { - final AST tree = new AST(new ASTNode("expr", 1)); + private static SyntaxTree tree2() { + final SyntaxTree tree = new SyntaxTree(new SyntaxTreeNode("expr", 1)); - final ASTNode right = new ASTNode("INTEGER_LIT", 1); + final SyntaxTreeNode right = new SyntaxTreeNode("INTEGER_LIT", 1); right.setValue("1"); - final ASTNode left = new ASTNode("expr", 1); + final SyntaxTreeNode left = new SyntaxTreeNode("expr", 1); left.setValue("SUB"); - final ASTNode lleft = new ASTNode("expr", 1); + final SyntaxTreeNode lleft = new SyntaxTreeNode("expr", 1); lleft.setValue("SUB"); - final ASTNode lright = new ASTNode("INTEGER_LIT", 1); + final SyntaxTreeNode lright = new SyntaxTreeNode("INTEGER_LIT", 1); lright.setValue("2"); - final ASTNode llleft = new ASTNode("INTEGER_LIT", 1); + final SyntaxTreeNode llleft = new SyntaxTreeNode("INTEGER_LIT", 1); llleft.setValue("3"); lleft.setChildren(llleft); @@ -54,22 +54,22 @@ class ASTBalancerTest { return tree; } - private static AST tree3() { - final AST tree = new AST(new ASTNode("expr", 1)); + private static SyntaxTree tree3() { + final SyntaxTree tree = new SyntaxTree(new SyntaxTreeNode("expr", 1)); - final ASTNode right = new ASTNode("INTEGER_LIT", 1); + final SyntaxTreeNode right = new SyntaxTreeNode("INTEGER_LIT", 1); right.setValue("1"); - final ASTNode left = new ASTNode("expr", 1); + final SyntaxTreeNode left = new SyntaxTreeNode("expr", 1); left.setValue("SUB"); - final ASTNode lleft = new ASTNode("expr", 1); + final SyntaxTreeNode lleft = new SyntaxTreeNode("expr", 1); lleft.setValue("MUL"); - final ASTNode lright = new ASTNode("INTEGER_LIT", 1); + final SyntaxTreeNode lright = new SyntaxTreeNode("INTEGER_LIT", 1); lright.setValue("2"); - final ASTNode llleft = new ASTNode("INTEGER_LIT", 1); + final SyntaxTreeNode llleft = new SyntaxTreeNode("INTEGER_LIT", 1); llleft.setValue("3"); lleft.setChildren(llleft); @@ -82,7 +82,7 @@ class ASTBalancerTest { @Test void testTree1Flip() { - final AST tree = tree1(); + final SyntaxTree tree = tree1(); ASTBalancer.flip(tree); @@ -92,7 +92,7 @@ class ASTBalancerTest { @Test void testTree1Flip2x() { - final AST tree = tree1(); + final SyntaxTree tree = tree1(); ASTBalancer.flip(tree); ASTBalancer.flip(tree); @@ -102,7 +102,7 @@ class ASTBalancerTest { @Test void testTree2Flip() { - final AST tree = tree2(); + final SyntaxTree tree = tree2(); ASTBalancer.flip(tree); @@ -114,7 +114,7 @@ class ASTBalancerTest { @Test void testTree2Flip2x() { - final AST tree = tree2(); + final SyntaxTree tree = tree2(); ASTBalancer.flip(tree); ASTBalancer.flip(tree); @@ -124,7 +124,7 @@ class ASTBalancerTest { @Test void testTree1LeftPrecedence() { - final AST tree = tree1(); + final SyntaxTree tree = tree1(); ASTBalancer.flip(tree); ASTBalancer.leftPrecedence(tree); @@ -135,7 +135,7 @@ class ASTBalancerTest { @Test void testTree2LeftPrecedence() { - final AST tree = tree2(); + final SyntaxTree tree = tree2(); ASTBalancer.flip(tree); ASTBalancer.leftPrecedence(tree); @@ -146,11 +146,11 @@ class ASTBalancerTest { @Test void testTree2OperatorPrecedence() { - final AST tree = tree2(); + final SyntaxTree tree = tree2(); ASTBalancer.flip(tree); ASTBalancer.leftPrecedence(tree); - final AST tree1 = tree2(); + final SyntaxTree tree1 = tree2(); ASTBalancer.flip(tree1); ASTBalancer.leftPrecedence(tree1); @@ -161,7 +161,7 @@ class ASTBalancerTest { @Test void testTree3OperatorPrecedence() { - final AST tree = tree3(); + final SyntaxTree tree = tree3(); ASTBalancer.flip(tree); ASTBalancer.leftPrecedence(tree); diff --git a/src/test/java/parser/ast/ASTCompacterTest.java b/src/test/java/parser/ast/SyntaxTreeCompacterTest.java similarity index 75% rename from src/test/java/parser/ast/ASTCompacterTest.java rename to src/test/java/parser/ast/SyntaxTreeCompacterTest.java index b440f9d..09b1e54 100644 --- a/src/test/java/parser/ast/ASTCompacterTest.java +++ b/src/test/java/parser/ast/SyntaxTreeCompacterTest.java @@ -17,21 +17,21 @@ import java.nio.file.Paths; import static org.assertj.core.api.Assertions.assertThat; -class ASTCompacterTest { +class SyntaxTreeCompacterTest { private static Grammar grammar; private static StupsParser parser; @BeforeAll static void init() throws IOException, URISyntaxException { - final Path path = Paths.get(ASTCompacterTest.class.getClassLoader().getResource("exampleGrammars/Grammar.grammar").toURI()); + final Path path = Paths.get(SyntaxTreeCompacterTest.class.getClassLoader().getResource("exampleGrammars/Grammar.grammar").toURI()); grammar = Grammar.fromFile(path); parser = StupsParser.fromGrammar(grammar); } - private static AST getTree(String program) { + private static SyntaxTree getTree(String program) { try { - final Path path = Paths.get(ASTCompacterTest.class.getClassLoader().getResource("examplePrograms/" + program).toURI()); + final Path path = Paths.get(SyntaxTreeCompacterTest.class.getClassLoader().getResource("examplePrograms/" + program).toURI()); final String programCode = Files.readString(path, StandardCharsets.US_ASCII); final Lexer lex = new StupsLexer(CharStreams.fromString(programCode)); return parser.parse(lex.getAllTokens(), lex.getVocabulary()); @@ -44,7 +44,7 @@ class ASTCompacterTest { @Test void testDeleteChildren() { - final AST tree = getTree("GeneralOperator.stups"); + final SyntaxTree tree = getTree("GeneralOperator.stups"); final long before = tree.size(); ASTCompacter.deleteChildren(tree, grammar); @@ -54,7 +54,7 @@ class ASTCompacterTest { @Test void testPromote() { - final AST tree = getTree("GeneralOperator.stups"); + final SyntaxTree tree = getTree("GeneralOperator.stups"); final long before = tree.size(); ASTCompacter.promote(tree, grammar); @@ -64,7 +64,7 @@ class ASTCompacterTest { @Test void testDeleteEmpty() { - final AST tree = getTree("GeneralOperator.stups"); + final SyntaxTree tree = getTree("GeneralOperator.stups"); ASTCompacter.deleteChildren(tree, grammar); final long before = tree.size(); @@ -75,7 +75,7 @@ class ASTCompacterTest { @Test void testClean() { - final AST tree = getTree("GeneralOperator.stups"); + final SyntaxTree tree = getTree("GeneralOperator.stups"); ASTCompacter.clean(tree, grammar); diff --git a/src/test/java/parser/ast/SyntaxTreeTest.java b/src/test/java/parser/ast/SyntaxTreeTest.java new file mode 100644 index 0000000..fde5ed2 --- /dev/null +++ b/src/test/java/parser/ast/SyntaxTreeTest.java @@ -0,0 +1,45 @@ +package parser.ast; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class SyntaxTreeTest { + + @Test + void testOneNode() { + final SyntaxTreeNode root = new SyntaxTreeNode("Wurzel", 1); + + final SyntaxTree tree = new SyntaxTree(root); + + assertThat(tree).hasToString("Wurzel\n"); + } + + @Test + void testThreeNodesBinary() { + final SyntaxTreeNode root = new SyntaxTreeNode("Wurzel", 1); + final SyntaxTreeNode childA = new SyntaxTreeNode("A", 1); + final SyntaxTreeNode childB = new SyntaxTreeNode("B", 1); + + root.addChild(childA); + root.addChild(childB); + + final SyntaxTree tree = new SyntaxTree(root); + + assertThat(tree).hasToString("Wurzel\n├── A\n└── B\n"); + } + + @Test + void testThreeNodesLinear() { + final SyntaxTreeNode root = new SyntaxTreeNode("Wurzel", 1); + final SyntaxTreeNode childA = new SyntaxTreeNode("A", 1); + final SyntaxTreeNode childB = new SyntaxTreeNode("B", 1); + + root.addChild(childA); + childA.addChild(childB); + + final SyntaxTree tree = new SyntaxTree(root); + + assertThat(tree).hasToString("Wurzel\n└── A\n └── B\n"); + } +} diff --git a/src/test/java/parser/typechecker/TypeCheckerTest.java b/src/test/java/parser/typechecker/TypeCheckerTest.java index 5d50bae..2bb2ef1 100644 --- a/src/test/java/parser/typechecker/TypeCheckerTest.java +++ b/src/test/java/parser/typechecker/TypeCheckerTest.java @@ -7,7 +7,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import parser.ParseException; import parser.StupsParser; -import parser.ast.AST; +import parser.ast.SyntaxTree; import parser.grammar.Grammar; import typechecker.AssignmentTypeMismatchException; import typechecker.OperatorTypeMismatchException; @@ -36,12 +36,12 @@ class TypeCheckerTest { + "}}"; } - private AST getTree(String expr) { + private SyntaxTree getTree(String expr) { final Lexer lex = new StupsLexer(CharStreams.fromString(exprToProg(expr))); - final AST tree = this.stupsParser.parse(lex.getAllTokens(), lex.getVocabulary()); - tree.postprocess(this.grammar); + final SyntaxTree tree = this.stupsParser.parse(lex.getAllTokens(), lex.getVocabulary()); + final SyntaxTree ast = SyntaxTree.toAbstractSyntaxTree(tree, this.grammar); - return tree; + return ast; } @ParameterizedTest diff --git a/src/test/java/parser/typechecker/TypeTableTest.java b/src/test/java/parser/typechecker/TypeTableTest.java index 52616b7..2580b1a 100644 --- a/src/test/java/parser/typechecker/TypeTableTest.java +++ b/src/test/java/parser/typechecker/TypeTableTest.java @@ -6,7 +6,7 @@ import org.antlr.v4.runtime.Lexer; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import parser.StupsParser; -import parser.ast.AST; +import parser.ast.SyntaxTree; import parser.grammar.Grammar; import typechecker.SymbolAlreadyDefinedException; import typechecker.TypeTable; @@ -33,14 +33,14 @@ class TypeTableTest { parser = StupsParser.fromGrammar(grammar); } - private static AST getTree(String program) { + private static SyntaxTree getTree(String program) { try { final Path path = Paths.get(TypeTableTest.class.getClassLoader().getResource("examplePrograms/" + program).toURI()); final String programCode = Files.readString(path, StandardCharsets.US_ASCII); final Lexer lex = new StupsLexer(CharStreams.fromString(programCode)); - final AST tree = parser.parse(lex.getAllTokens(), lex.getVocabulary()); - tree.postprocess(grammar); - return tree; + final SyntaxTree tree = parser.parse(lex.getAllTokens(), lex.getVocabulary()); + final SyntaxTree ast = SyntaxTree.toAbstractSyntaxTree(tree, grammar); + return ast; } catch (Exception ignore) { ignore.printStackTrace(); } @@ -50,7 +50,7 @@ class TypeTableTest { @Test void testSingleSymbol() { - final AST tree = getTree("SingleSymbol.stups"); + final SyntaxTree tree = getTree("SingleSymbol.stups"); final TypeTable table = TypeTable.fromAST(tree); @@ -60,7 +60,7 @@ class TypeTableTest { @Test void testMultipleSymbol() { - final AST tree = getTree("MultipleSymbol.stups"); + final SyntaxTree tree = getTree("MultipleSymbol.stups"); final TypeTable table = TypeTable.fromAST(tree); @@ -75,14 +75,14 @@ class TypeTableTest { @Test void testExistingSymbol() { - final AST tree = getTree("ExistingSymbol.stups"); + final SyntaxTree tree = getTree("ExistingSymbol.stups"); assertThatThrownBy(() -> TypeTable.fromAST(tree)).isInstanceOf(SymbolAlreadyDefinedException.class); } @Test void testExistingSymbol2() { - final AST tree = getTree("ExistingSymbol2.stups"); + final SyntaxTree tree = getTree("ExistingSymbol2.stups"); assertThatThrownBy(() -> TypeTable.fromAST(tree)).isInstanceOf(SymbolAlreadyDefinedException.class); }