ParseTreeCleaner + SyntaxTreeRebalancer
This commit is contained in:
@ -7,28 +7,42 @@ import java.util.HashSet;
|
|||||||
|
|
||||||
import static util.Logger.log;
|
import static util.Logger.log;
|
||||||
|
|
||||||
public final class ASTCompacter {
|
/**
|
||||||
|
* Wendet in der Grammatik definierte Regeln auf einen Parsebaum an.
|
||||||
|
* Dies ist der erste Schritt zum Abstrakten Syntaxbaum.
|
||||||
|
*
|
||||||
|
* <ul>
|
||||||
|
* <li>Löscht redundante Knoten</li>
|
||||||
|
* <li>Löscht leere Knoten</li>
|
||||||
|
* <li>Komprimiert Äste, welche nur Informationen hochpropagieren</li>
|
||||||
|
* <li>Führt Umbenennungen durch</li>
|
||||||
|
* <li>Verschiebt Informationen in Knoten-Namen und -Wert</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public final class ParseTreeCleaner {
|
||||||
|
|
||||||
private ASTCompacter() {}
|
private ParseTreeCleaner() {}
|
||||||
|
|
||||||
public static void clean(SyntaxTree tree, Grammar grammar) {
|
public static void clean(SyntaxTree parseTree, Grammar grammar) {
|
||||||
deleteChildren(tree, grammar);
|
deleteChildren(parseTree, grammar);
|
||||||
deleteIfEmpty(tree, grammar);
|
deleteIfEmpty(parseTree, grammar);
|
||||||
promote(tree, grammar);
|
promote(parseTree, grammar);
|
||||||
|
|
||||||
renameTo(tree, grammar);
|
renameTo(parseTree, grammar);
|
||||||
nameToValue(tree, grammar);
|
nameToValue(parseTree, grammar);
|
||||||
valueToValue(tree, grammar);
|
valueToValue(parseTree, grammar);
|
||||||
|
|
||||||
log("\nCleaned Tree:\n" + tree);
|
log("\nCleaned Tree:\n" + parseTree);
|
||||||
log("-".repeat(100));
|
log("-".repeat(100));
|
||||||
System.out.println(" - Compressing syntax-tree...");
|
System.out.println(" - Compressing syntax-tree...");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entfernt [promote]-able Nodes (Reicht Werte nach oben)
|
/**
|
||||||
public static void promote(SyntaxTree tree, Grammar grammar) {
|
* Es werden Werte nach oben gereicht von [promote]-able Nodes.
|
||||||
|
*/
|
||||||
|
public static void promote(SyntaxTree parseTree, Grammar grammar) {
|
||||||
log("\nPromoting nodes:");
|
log("\nPromoting nodes:");
|
||||||
promote(tree.getRoot(), grammar);
|
promote(parseTree.getRoot(), grammar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void promote(SyntaxTreeNode root, Grammar grammar) {
|
private static void promote(SyntaxTreeNode root, Grammar grammar) {
|
||||||
@ -49,17 +63,19 @@ public final class ASTCompacter {
|
|||||||
root.setValue(child.getValue());
|
root.setValue(child.getValue());
|
||||||
root.setChildren(child.getChildren());
|
root.setChildren(child.getChildren());
|
||||||
|
|
||||||
child.setValue("REMOVE"); // If both childs have the same identity both are removed
|
child.setValue("REMOVE"); // If both childs have the same identity both are removed, so change one
|
||||||
toRemove.add(child);
|
toRemove.add(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
root.getChildren().removeAll(toRemove);
|
root.getChildren().removeAll(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Entfernt [delIfEmpty] Nodes (löscht Nodes ohne Inhalt)
|
/**
|
||||||
public static void deleteIfEmpty(SyntaxTree tree, Grammar grammar) {
|
* Löscht leere Knoten mit [delIfEmpty].
|
||||||
|
*/
|
||||||
|
public static void deleteIfEmpty(SyntaxTree parseTree, Grammar grammar) {
|
||||||
log("\nDeleting empty nodes:");
|
log("\nDeleting empty nodes:");
|
||||||
deleteIfEmpty(tree.getRoot(), grammar);
|
deleteIfEmpty(parseTree.getRoot(), grammar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void deleteIfEmpty(SyntaxTreeNode root, Grammar grammar) {
|
private static void deleteIfEmpty(SyntaxTreeNode root, Grammar grammar) {
|
||||||
@ -74,17 +90,19 @@ public final class ASTCompacter {
|
|||||||
|
|
||||||
log("Removing " + child.getName());
|
log("Removing " + child.getName());
|
||||||
|
|
||||||
child.setValue("REMOVE"); // If both childs have the same identity both are removed
|
child.setValue("REMOVE"); // If both childs have the same identity both are removed, so change one
|
||||||
toRemove.add(child);
|
toRemove.add(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
root.getChildren().removeAll(toRemove);
|
root.getChildren().removeAll(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Löscht redundante Informationen in [delChildren]-Nodes (z.b. IF-child von COND) und Epsilon-Nodes
|
/**
|
||||||
public static void deleteChildren(SyntaxTree tree, Grammar grammar) {
|
* 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:");
|
log("Removing redundant children:");
|
||||||
deleteChildren(tree.getRoot(), grammar);
|
deleteChildren(parseTree.getRoot(), grammar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void deleteChildren(SyntaxTreeNode root, Grammar grammar) {
|
private static void deleteChildren(SyntaxTreeNode root, Grammar grammar) {
|
||||||
@ -99,17 +117,19 @@ public final class ASTCompacter {
|
|||||||
|
|
||||||
log("Removing " + root.getName() + " -> " + child.getName());
|
log("Removing " + root.getName() + " -> " + child.getName());
|
||||||
|
|
||||||
child.setValue("REMOVE"); // If both childs have the same identity both are removed
|
child.setValue("REMOVE"); // If both childs have the same identity both are removed, so change one
|
||||||
toRemove.add(child);
|
toRemove.add(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
root.getChildren().removeAll(toRemove);
|
root.getChildren().removeAll(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Umbenennungen
|
/**
|
||||||
private static void renameTo(SyntaxTree tree, Grammar grammar) {
|
* Führt Umbenennungen durch.
|
||||||
|
*/
|
||||||
|
private static void renameTo(SyntaxTree parseTree, Grammar grammar) {
|
||||||
log("\nRenaming nodes:");
|
log("\nRenaming nodes:");
|
||||||
renameTo(tree.getRoot(), grammar);
|
renameTo(parseTree.getRoot(), grammar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void renameTo(SyntaxTreeNode root, Grammar grammar) {
|
private static void renameTo(SyntaxTreeNode root, Grammar grammar) {
|
||||||
@ -126,9 +146,12 @@ public final class ASTCompacter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void nameToValue(SyntaxTree tree, Grammar grammar) {
|
/**
|
||||||
|
* 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:");
|
log("\nMoving names to values:");
|
||||||
nameToValue(tree.getRoot(), grammar);
|
nameToValue(parseTree.getRoot(), grammar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void nameToValue(SyntaxTreeNode root, Grammar grammar) {
|
private static void nameToValue(SyntaxTreeNode root, Grammar grammar) {
|
||||||
@ -146,17 +169,21 @@ public final class ASTCompacter {
|
|||||||
|
|
||||||
root.setValue(child.getName());
|
root.setValue(child.getName());
|
||||||
|
|
||||||
child.setValue("REMOVE"); // If both childs have the same identity both are removed
|
child.setValue("REMOVE"); // If both childs have the same identity both are removed, so change one
|
||||||
toRemove.add(child);
|
toRemove.add(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
root.getChildren().removeAll(toRemove);
|
root.getChildren().removeAll(toRemove);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Assignment bekommt den Identifier als Value anstatt als Child
|
/**
|
||||||
public static void valueToValue(SyntaxTree tree, Grammar grammar) {
|
* [valtoval]-Nodes bekommen den Child-Namen als Value anstatt als Child.
|
||||||
|
* Wird z.B. durchgeführt bei Assignments: Der Assignment-Node bekommt den
|
||||||
|
* Variablennamen als Wert anstatt als Child-Node.
|
||||||
|
*/
|
||||||
|
public static void valueToValue(SyntaxTree parseTree, Grammar grammar) {
|
||||||
log("\nMoving values to values:");
|
log("\nMoving values to values:");
|
||||||
valueToValue(tree.getRoot(), grammar);
|
valueToValue(parseTree.getRoot(), grammar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void valueToValue(SyntaxTreeNode root, Grammar grammar) {
|
private static void valueToValue(SyntaxTreeNode root, Grammar grammar) {
|
||||||
@ -165,36 +192,31 @@ public final class ASTCompacter {
|
|||||||
for (SyntaxTreeNode child : root.getChildren()) {
|
for (SyntaxTreeNode child : root.getChildren()) {
|
||||||
valueToValue(child, grammar);
|
valueToValue(child, grammar);
|
||||||
|
|
||||||
if (!grammar.hasValToVal(root, child)) {
|
if (!grammar.hasValToVal(root, child) || !root.getValue().isBlank()) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!root.getValue().isBlank()) {
|
|
||||||
// Do not overwrite
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (root.getChildren().size() == 2
|
if (root.getChildren().size() == 2
|
||||||
&& root.getChildren().get(0).getName().equals(root.getChildren().get(1).getName())) {
|
&& root.getChildren().get(0).getName().equals(root.getChildren().get(1).getName())) {
|
||||||
// Special case where variable is assigned another variable
|
// Case where variable is assigned another variable with the same name
|
||||||
|
|
||||||
log("Special case: Var to var assignment");
|
|
||||||
log("Moving " + root.getChildren().get(1).getValue() + " to value of " + root.getName());
|
log("Moving " + root.getChildren().get(1).getValue() + " to value of " + root.getName());
|
||||||
log(root.toString());
|
log(root.toString());
|
||||||
|
|
||||||
root.setValue(root.getChildren().get(1).getValue());
|
root.setValue(root.getChildren().get(1).getValue());
|
||||||
|
|
||||||
root.getChildren().get(1).setValue("REMOVE"); // If both childs have the same identity both are removed
|
root.getChildren().get(1).setValue("REMOVE"); // If both childs have the same identity both are removed, so change one
|
||||||
toRemove.add(root.getChildren().get(1));
|
toRemove.add(root.getChildren().get(1));
|
||||||
|
|
||||||
continue;
|
} else {
|
||||||
|
// Usual case where an expression is assigned
|
||||||
|
|
||||||
|
log("Moving " + child.getValue() + " to value of " + root.getName());
|
||||||
|
log(root.toString());
|
||||||
|
|
||||||
|
root.setValue(child.getValue());
|
||||||
|
toRemove.add(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
log("Moving " + child.getValue() + " to value of " + root.getName());
|
|
||||||
log(root.toString());
|
|
||||||
|
|
||||||
root.setValue(child.getValue());
|
|
||||||
toRemove.add(child);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
root.getChildren().removeAll(toRemove);
|
root.getChildren().removeAll(toRemove);
|
@ -23,8 +23,8 @@ public class SyntaxTree {
|
|||||||
public static SyntaxTree toAbstractSyntaxTree(SyntaxTree concreteSyntaxTree, Grammar grammar) {
|
public static SyntaxTree toAbstractSyntaxTree(SyntaxTree concreteSyntaxTree, Grammar grammar) {
|
||||||
final SyntaxTree abstractSyntaxTree = concreteSyntaxTree.deepCopy();
|
final SyntaxTree abstractSyntaxTree = concreteSyntaxTree.deepCopy();
|
||||||
|
|
||||||
ASTCompacter.clean(abstractSyntaxTree, grammar);
|
ParseTreeCleaner.clean(abstractSyntaxTree, grammar);
|
||||||
ASTBalancer.balance(abstractSyntaxTree);
|
SyntaxTreeRebalancer.rebalance(abstractSyntaxTree);
|
||||||
System.out.println("Tree processing successful.");
|
System.out.println("Tree processing successful.");
|
||||||
|
|
||||||
return abstractSyntaxTree;
|
return abstractSyntaxTree;
|
||||||
|
@ -6,11 +6,19 @@ import java.util.Set;
|
|||||||
|
|
||||||
import static util.Logger.log;
|
import static util.Logger.log;
|
||||||
|
|
||||||
public final class ASTBalancer {
|
/**
|
||||||
|
* Ein SyntaxTree wird an bestimmten Stellen rotiert, sodass bestimmte Eigenschaften
|
||||||
|
* korrekt repräsentiert werden (Operatorpräzedenz, Linkassoziativität etc.).
|
||||||
|
*/
|
||||||
|
public final class SyntaxTreeRebalancer {
|
||||||
|
|
||||||
private static final Map<String, Integer> priority;
|
/**
|
||||||
private static final Set<String> unary;
|
* Jedem Operator wird eine Priorität zugewiesen, 0 ist die höchste.
|
||||||
private static final Set<String> commutative;
|
*/
|
||||||
|
private static final Map<String, Integer> operatorPriority;
|
||||||
|
|
||||||
|
private static final Set<String> unaryOperators;
|
||||||
|
private static final Set<String> commutativeOperators;
|
||||||
|
|
||||||
//!: Operatorpräzedenz
|
//!: Operatorpräzedenz
|
||||||
// 0 - Unary: -, +, !
|
// 0 - Unary: -, +, !
|
||||||
@ -21,44 +29,56 @@ public final class ASTBalancer {
|
|||||||
// 5 - Logical AND: &&
|
// 5 - Logical AND: &&
|
||||||
// 6 - Logical OR: ||
|
// 6 - Logical OR: ||
|
||||||
static {
|
static {
|
||||||
priority = Map.ofEntries(Map.entry("NOT", 0),
|
operatorPriority = Map.ofEntries(Map.entry("NOT", 0),
|
||||||
Map.entry("MUL", 1),
|
Map.entry("MUL", 1),
|
||||||
Map.entry("DIV", 1),
|
Map.entry("DIV", 1),
|
||||||
Map.entry("MOD", 1),
|
Map.entry("MOD", 1),
|
||||||
Map.entry("ADD", 2),
|
Map.entry("ADD", 2),
|
||||||
Map.entry("SUB", 2),
|
Map.entry("SUB", 2),
|
||||||
Map.entry("LESS", 3),
|
Map.entry("LESS", 3),
|
||||||
Map.entry("LESS_EQUAL", 3),
|
Map.entry("LESS_EQUAL", 3),
|
||||||
Map.entry("GREATER", 3),
|
Map.entry("GREATER", 3),
|
||||||
Map.entry("GREATER_EQUAL", 3),
|
Map.entry("GREATER_EQUAL", 3),
|
||||||
Map.entry("EQUAL", 4),
|
Map.entry("EQUAL", 4),
|
||||||
Map.entry("NOT_EQUAL", 4),
|
Map.entry("NOT_EQUAL", 4),
|
||||||
Map.entry("AND", 5),
|
Map.entry("AND", 5),
|
||||||
Map.entry("OR", 6));
|
Map.entry("OR", 6));
|
||||||
|
|
||||||
unary = Set.of("NOT", "ADD", "SUB");
|
unaryOperators = Set.of("NOT", "ADD", "SUB");
|
||||||
|
|
||||||
commutative = Set.of("ADD", "MUL", "EQUAL", "NOT_EQUAL", "AND", "OR");
|
commutativeOperators = Set.of("ADD", "MUL", "EQUAL", "NOT_EQUAL", "AND", "OR");
|
||||||
}
|
}
|
||||||
|
|
||||||
private ASTBalancer() {}
|
private SyntaxTreeRebalancer() {}
|
||||||
|
|
||||||
public static void balance(SyntaxTree tree) {
|
/**
|
||||||
flip(tree);
|
* Ein Abstrakter Syntaxbaum wird umbalanciert.
|
||||||
leftPrecedence(tree);
|
*
|
||||||
operatorPrecedence(tree);
|
* <ul>
|
||||||
flipCommutativeExpr(tree);
|
* <li>Baum wird gespiegelt, damit die Ausdrücke vorwárts laufen (Tiefste Ebenen müssen nach links)</li>
|
||||||
|
* <li>Linkspräzedenz wird durch Links-Rotationen durchgesetzt</li>
|
||||||
|
* <li>Operatorpräzedenz wird durch Rechtsrotationen durchgesetzt</li>
|
||||||
|
* <li>Kommutative Ausdrücke werden gespiegelt, damit die tiefen Teilausdrücke zuerst berechnet werden</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
public static void rebalance(SyntaxTree abstractSyntaxTree) {
|
||||||
|
flip(abstractSyntaxTree);
|
||||||
|
leftPrecedence(abstractSyntaxTree);
|
||||||
|
operatorPrecedence(abstractSyntaxTree);
|
||||||
|
flipCommutativeExpr(abstractSyntaxTree);
|
||||||
|
|
||||||
log(tree.toString());
|
log(abstractSyntaxTree.toString());
|
||||||
log("-".repeat(100));
|
log("-".repeat(100));
|
||||||
|
|
||||||
System.out.println(" - Balancing syntax-tree...");
|
System.out.println(" - Balancing syntax-tree...");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Baum spiegeln, damit höhere Ebenen links sind und EXPR vorwärts laufen
|
/**
|
||||||
public static void flip(SyntaxTree tree) {
|
* 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");
|
log("Flipping tree for ltr evaluation");
|
||||||
flip(tree.getRoot());
|
flip(abstractSyntaxTree.getRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void flip(SyntaxTreeNode root) {
|
private static void flip(SyntaxTreeNode root) {
|
||||||
@ -69,9 +89,12 @@ public final class ASTBalancer {
|
|||||||
Collections.reverse(root.getChildren());
|
Collections.reverse(root.getChildren());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void flipCommutativeExpr(SyntaxTree tree) {
|
/**
|
||||||
|
* 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");
|
log("Flipping commutative expressions for stack efficiency");
|
||||||
flipCommutativeExpr(tree.getRoot());
|
flipCommutativeExpr(abstractSyntaxTree.getRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void flipCommutativeExpr(SyntaxTreeNode root) {
|
private static void flipCommutativeExpr(SyntaxTreeNode root) {
|
||||||
@ -79,9 +102,12 @@ public final class ASTBalancer {
|
|||||||
flipCommutativeExpr(child);
|
flipCommutativeExpr(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ("expr".equals(root.getName()) && commutative.contains(root.getValue())) {
|
if ("expr".equals(root.getName()) && commutativeOperators.contains(root.getValue())) {
|
||||||
|
// Ausdruck ist kommutativ
|
||||||
|
|
||||||
if (root.getChildren().size() == 2 && root.getChildren().get(0).size() < root.getChildren().get(1).size()) {
|
if (root.getChildren().size() == 2 && root.getChildren().get(0).size() < root.getChildren().get(1).size()) {
|
||||||
// Make the bigger subtree the left one
|
// Make the bigger subtree the left one
|
||||||
|
|
||||||
log("Flipping " + root.getName() + ": " + root.getValue() + " for stack efficiency.");
|
log("Flipping " + root.getName() + ": " + root.getValue() + " for stack efficiency.");
|
||||||
log(root.toString());
|
log(root.toString());
|
||||||
|
|
||||||
@ -90,14 +116,15 @@ public final class ASTBalancer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Führt Linksrotationen durch
|
/**
|
||||||
// Es werden EXPR-Nodes (2 Childs, 1 davon EXPR, Kein Wert) solange wie möglich linksrotiert
|
* Führt Linksrotationen durch für Linkspräzedenz.
|
||||||
public static void leftPrecedence(SyntaxTree tree) {
|
* 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");
|
log("Left-rotating expressions for left-precedence");
|
||||||
leftPrecedence(tree.getRoot());
|
leftPrecedence(abstractSyntaxTree.getRoot());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Es wird solange rotiert bis die letzte "Rotation" durchgeführt wurde
|
|
||||||
private static void leftPrecedence(SyntaxTreeNode root) {
|
private static void leftPrecedence(SyntaxTreeNode root) {
|
||||||
for (SyntaxTreeNode child : root.getChildren()) {
|
for (SyntaxTreeNode child : root.getChildren()) {
|
||||||
leftPrecedence(child);
|
leftPrecedence(child);
|
||||||
@ -116,7 +143,12 @@ public final class ASTBalancer {
|
|||||||
} while (change);
|
} while (change);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Die Letzte Rotation ist keine richtige Rotation, dort wird false zurückgegeben
|
/**
|
||||||
|
* Führt eine Linksrotation durch.
|
||||||
|
* Diese ist nicht regulär, da der Operator linksvererbt wird.
|
||||||
|
*
|
||||||
|
* @return Es wird false zurückgegeben, sobald keine weitere Rotation mehr möglich ist.
|
||||||
|
*/
|
||||||
private static boolean specialLeftRotate(SyntaxTreeNode root) {
|
private static boolean specialLeftRotate(SyntaxTreeNode root) {
|
||||||
log("Special-Left-Rotation around " + root.getName());
|
log("Special-Left-Rotation around " + root.getName());
|
||||||
log(root.toString());
|
log(root.toString());
|
||||||
@ -157,13 +189,18 @@ public final class ASTBalancer {
|
|||||||
return root.getChildren().size() == 1;
|
return root.getChildren().size() == 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void operatorPrecedence(SyntaxTree tree) {
|
/**
|
||||||
|
* Führt Rechtsrotationen durch für Operatorpräzedenz.
|
||||||
|
* Es wird solange rechtsrotiert, bis alle Operatoren mit hoher Priorität tiefer stehen
|
||||||
|
* als die Operatoren mit niedriger Priorität.
|
||||||
|
*/
|
||||||
|
public static void operatorPrecedence(SyntaxTree abstractSyntaxTree) {
|
||||||
log("Right-rotating expressions for operator-precedence");
|
log("Right-rotating expressions for operator-precedence");
|
||||||
|
|
||||||
boolean changed;
|
boolean changed;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
changed = operatorPrecedence(tree.getRoot());
|
changed = operatorPrecedence(abstractSyntaxTree.getRoot());
|
||||||
} while (changed);
|
} while (changed);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,6 +219,9 @@ public final class ASTBalancer {
|
|||||||
return changed;
|
return changed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ermittelt, ob der ParentNode höhere Priorität als der ChildNode hat.
|
||||||
|
*/
|
||||||
private static boolean preceding(SyntaxTreeNode parent, SyntaxTreeNode child) {
|
private static boolean preceding(SyntaxTreeNode parent, SyntaxTreeNode child) {
|
||||||
if (!"expr".equals(parent.getName()) || parent.getValue().isEmpty()
|
if (!"expr".equals(parent.getName()) || parent.getValue().isEmpty()
|
||||||
|| !"expr".equals(child.getName()) || child.getValue().isEmpty()) {
|
|| !"expr".equals(child.getName()) || child.getValue().isEmpty()) {
|
||||||
@ -189,13 +229,13 @@ public final class ASTBalancer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unary operators have the highest precedence
|
// Unary operators have the highest precedence
|
||||||
if (child.getChildren().size() == 1 && unary.contains(child.getValue())) {
|
if (child.getChildren().size() == 1 && unaryOperators.contains(child.getValue())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Less equals higher
|
// Less equals higher
|
||||||
{
|
{
|
||||||
return priority.get(parent.getValue()) < priority.get(child.getValue());
|
return operatorPriority.get(parent.getValue()) < operatorPriority.get(child.getValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -17,21 +17,21 @@ import java.nio.file.Paths;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class SyntaxTreeCompacterTest {
|
class ParseTreeCleanerTest {
|
||||||
|
|
||||||
private static Grammar grammar;
|
private static Grammar grammar;
|
||||||
private static StupsParser parser;
|
private static StupsParser parser;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
static void init() throws IOException, URISyntaxException {
|
static void init() throws IOException, URISyntaxException {
|
||||||
final Path path = Paths.get(SyntaxTreeCompacterTest.class.getClassLoader().getResource("exampleGrammars/Grammar.grammar").toURI());
|
final Path path = Paths.get(ParseTreeCleanerTest.class.getClassLoader().getResource("exampleGrammars/Grammar.grammar").toURI());
|
||||||
grammar = Grammar.fromFile(path);
|
grammar = Grammar.fromFile(path);
|
||||||
parser = StupsParser.fromGrammar(grammar);
|
parser = StupsParser.fromGrammar(grammar);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static SyntaxTree getTree(String program) {
|
private static SyntaxTree getTree(String program) {
|
||||||
try {
|
try {
|
||||||
final Path path = Paths.get(SyntaxTreeCompacterTest.class.getClassLoader().getResource("examplePrograms/" + program).toURI());
|
final Path path = Paths.get(ParseTreeCleanerTest.class.getClassLoader().getResource("examplePrograms/" + program).toURI());
|
||||||
final String programCode = Files.readString(path, StandardCharsets.US_ASCII);
|
final String programCode = Files.readString(path, StandardCharsets.US_ASCII);
|
||||||
final Lexer lex = new StupsLexer(CharStreams.fromString(programCode));
|
final Lexer lex = new StupsLexer(CharStreams.fromString(programCode));
|
||||||
return parser.parse(lex.getAllTokens(), lex.getVocabulary());
|
return parser.parse(lex.getAllTokens(), lex.getVocabulary());
|
||||||
@ -47,7 +47,7 @@ class SyntaxTreeCompacterTest {
|
|||||||
final SyntaxTree tree = getTree("GeneralOperator.stups");
|
final SyntaxTree tree = getTree("GeneralOperator.stups");
|
||||||
final long before = tree.size();
|
final long before = tree.size();
|
||||||
|
|
||||||
ASTCompacter.deleteChildren(tree, grammar);
|
ParseTreeCleaner.deleteChildren(tree, grammar);
|
||||||
|
|
||||||
assertThat(before - tree.size()).isEqualTo(3);
|
assertThat(before - tree.size()).isEqualTo(3);
|
||||||
}
|
}
|
||||||
@ -57,7 +57,7 @@ class SyntaxTreeCompacterTest {
|
|||||||
final SyntaxTree tree = getTree("GeneralOperator.stups");
|
final SyntaxTree tree = getTree("GeneralOperator.stups");
|
||||||
final long before = tree.size();
|
final long before = tree.size();
|
||||||
|
|
||||||
ASTCompacter.promote(tree, grammar);
|
ParseTreeCleaner.promote(tree, grammar);
|
||||||
|
|
||||||
assertThat(before - tree.size()).isEqualTo(14);
|
assertThat(before - tree.size()).isEqualTo(14);
|
||||||
}
|
}
|
||||||
@ -65,10 +65,10 @@ class SyntaxTreeCompacterTest {
|
|||||||
@Test
|
@Test
|
||||||
void testDeleteEmpty() {
|
void testDeleteEmpty() {
|
||||||
final SyntaxTree tree = getTree("GeneralOperator.stups");
|
final SyntaxTree tree = getTree("GeneralOperator.stups");
|
||||||
ASTCompacter.deleteChildren(tree, grammar);
|
ParseTreeCleaner.deleteChildren(tree, grammar);
|
||||||
final long before = tree.size();
|
final long before = tree.size();
|
||||||
|
|
||||||
ASTCompacter.deleteIfEmpty(tree, grammar);
|
ParseTreeCleaner.deleteIfEmpty(tree, grammar);
|
||||||
|
|
||||||
assertThat(before - tree.size()).isEqualTo(2);
|
assertThat(before - tree.size()).isEqualTo(2);
|
||||||
}
|
}
|
||||||
@ -77,7 +77,7 @@ class SyntaxTreeCompacterTest {
|
|||||||
void testClean() {
|
void testClean() {
|
||||||
final SyntaxTree tree = getTree("GeneralOperator.stups");
|
final SyntaxTree tree = getTree("GeneralOperator.stups");
|
||||||
|
|
||||||
ASTCompacter.clean(tree, grammar);
|
ParseTreeCleaner.clean(tree, grammar);
|
||||||
|
|
||||||
assertThat(tree.size()).isEqualTo(28);
|
assertThat(tree.size()).isEqualTo(28);
|
||||||
}
|
}
|
@ -4,7 +4,7 @@ import org.junit.jupiter.api.Test;
|
|||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
class SyntaxTreeBalancerTest {
|
class SyntaxTreeRebalancerTest {
|
||||||
|
|
||||||
//expr
|
//expr
|
||||||
//├── expr: SUB
|
//├── expr: SUB
|
||||||
@ -84,7 +84,7 @@ class SyntaxTreeBalancerTest {
|
|||||||
void testTree1Flip() {
|
void testTree1Flip() {
|
||||||
final SyntaxTree tree = tree1();
|
final SyntaxTree tree = tree1();
|
||||||
|
|
||||||
ASTBalancer.flip(tree);
|
SyntaxTreeRebalancer.flip(tree);
|
||||||
|
|
||||||
assertThat(tree.getRoot().getChildren().get(0).getName()).isEqualTo("INTEGER_LIT");
|
assertThat(tree.getRoot().getChildren().get(0).getName()).isEqualTo("INTEGER_LIT");
|
||||||
assertThat(tree.getRoot().getChildren().get(1).getName()).isEqualTo("expr");
|
assertThat(tree.getRoot().getChildren().get(1).getName()).isEqualTo("expr");
|
||||||
@ -94,8 +94,8 @@ class SyntaxTreeBalancerTest {
|
|||||||
void testTree1Flip2x() {
|
void testTree1Flip2x() {
|
||||||
final SyntaxTree tree = tree1();
|
final SyntaxTree tree = tree1();
|
||||||
|
|
||||||
ASTBalancer.flip(tree);
|
SyntaxTreeRebalancer.flip(tree);
|
||||||
ASTBalancer.flip(tree);
|
SyntaxTreeRebalancer.flip(tree);
|
||||||
|
|
||||||
assertThat(tree).isEqualTo(tree1());
|
assertThat(tree).isEqualTo(tree1());
|
||||||
}
|
}
|
||||||
@ -104,7 +104,7 @@ class SyntaxTreeBalancerTest {
|
|||||||
void testTree2Flip() {
|
void testTree2Flip() {
|
||||||
final SyntaxTree tree = tree2();
|
final SyntaxTree tree = tree2();
|
||||||
|
|
||||||
ASTBalancer.flip(tree);
|
SyntaxTreeRebalancer.flip(tree);
|
||||||
|
|
||||||
assertThat(tree.getRoot().getChildren().get(0).getName()).isEqualTo("INTEGER_LIT");
|
assertThat(tree.getRoot().getChildren().get(0).getName()).isEqualTo("INTEGER_LIT");
|
||||||
assertThat(tree.getRoot().getChildren().get(1).getName()).isEqualTo("expr");
|
assertThat(tree.getRoot().getChildren().get(1).getName()).isEqualTo("expr");
|
||||||
@ -116,8 +116,8 @@ class SyntaxTreeBalancerTest {
|
|||||||
void testTree2Flip2x() {
|
void testTree2Flip2x() {
|
||||||
final SyntaxTree tree = tree2();
|
final SyntaxTree tree = tree2();
|
||||||
|
|
||||||
ASTBalancer.flip(tree);
|
SyntaxTreeRebalancer.flip(tree);
|
||||||
ASTBalancer.flip(tree);
|
SyntaxTreeRebalancer.flip(tree);
|
||||||
|
|
||||||
assertThat(tree).isEqualTo(tree2());
|
assertThat(tree).isEqualTo(tree2());
|
||||||
}
|
}
|
||||||
@ -125,9 +125,9 @@ class SyntaxTreeBalancerTest {
|
|||||||
@Test
|
@Test
|
||||||
void testTree1LeftPrecedence() {
|
void testTree1LeftPrecedence() {
|
||||||
final SyntaxTree tree = tree1();
|
final SyntaxTree tree = tree1();
|
||||||
ASTBalancer.flip(tree);
|
SyntaxTreeRebalancer.flip(tree);
|
||||||
|
|
||||||
ASTBalancer.leftPrecedence(tree);
|
SyntaxTreeRebalancer.leftPrecedence(tree);
|
||||||
|
|
||||||
assertThat(tree.size()).isEqualTo(3);
|
assertThat(tree.size()).isEqualTo(3);
|
||||||
assertThat(tree.getRoot().getValue()).isEqualTo("SUB");
|
assertThat(tree.getRoot().getValue()).isEqualTo("SUB");
|
||||||
@ -136,9 +136,9 @@ class SyntaxTreeBalancerTest {
|
|||||||
@Test
|
@Test
|
||||||
void testTree2LeftPrecedence() {
|
void testTree2LeftPrecedence() {
|
||||||
final SyntaxTree tree = tree2();
|
final SyntaxTree tree = tree2();
|
||||||
ASTBalancer.flip(tree);
|
SyntaxTreeRebalancer.flip(tree);
|
||||||
|
|
||||||
ASTBalancer.leftPrecedence(tree);
|
SyntaxTreeRebalancer.leftPrecedence(tree);
|
||||||
|
|
||||||
assertThat(tree.size()).isEqualTo(5);
|
assertThat(tree.size()).isEqualTo(5);
|
||||||
assertThat(tree.getRoot().getValue()).isEqualTo("SUB");
|
assertThat(tree.getRoot().getValue()).isEqualTo("SUB");
|
||||||
@ -147,14 +147,14 @@ class SyntaxTreeBalancerTest {
|
|||||||
@Test
|
@Test
|
||||||
void testTree2OperatorPrecedence() {
|
void testTree2OperatorPrecedence() {
|
||||||
final SyntaxTree tree = tree2();
|
final SyntaxTree tree = tree2();
|
||||||
ASTBalancer.flip(tree);
|
SyntaxTreeRebalancer.flip(tree);
|
||||||
ASTBalancer.leftPrecedence(tree);
|
SyntaxTreeRebalancer.leftPrecedence(tree);
|
||||||
|
|
||||||
final SyntaxTree tree1 = tree2();
|
final SyntaxTree tree1 = tree2();
|
||||||
ASTBalancer.flip(tree1);
|
SyntaxTreeRebalancer.flip(tree1);
|
||||||
ASTBalancer.leftPrecedence(tree1);
|
SyntaxTreeRebalancer.leftPrecedence(tree1);
|
||||||
|
|
||||||
ASTBalancer.operatorPrecedence(tree);
|
SyntaxTreeRebalancer.operatorPrecedence(tree);
|
||||||
|
|
||||||
assertThat(tree).isEqualTo(tree1);
|
assertThat(tree).isEqualTo(tree1);
|
||||||
}
|
}
|
||||||
@ -162,12 +162,12 @@ class SyntaxTreeBalancerTest {
|
|||||||
@Test
|
@Test
|
||||||
void testTree3OperatorPrecedence() {
|
void testTree3OperatorPrecedence() {
|
||||||
final SyntaxTree tree = tree3();
|
final SyntaxTree tree = tree3();
|
||||||
ASTBalancer.flip(tree);
|
SyntaxTreeRebalancer.flip(tree);
|
||||||
ASTBalancer.leftPrecedence(tree);
|
SyntaxTreeRebalancer.leftPrecedence(tree);
|
||||||
|
|
||||||
assertThat(tree.getRoot().getValue()).isEqualTo("MUL");
|
assertThat(tree.getRoot().getValue()).isEqualTo("MUL");
|
||||||
|
|
||||||
ASTBalancer.operatorPrecedence(tree);
|
SyntaxTreeRebalancer.operatorPrecedence(tree);
|
||||||
|
|
||||||
assertThat(tree.size()).isEqualTo(5);
|
assertThat(tree.size()).isEqualTo(5);
|
||||||
assertThat(tree.getRoot().getValue()).isEqualTo("SUB");
|
assertThat(tree.getRoot().getValue()).isEqualTo("SUB");
|
Reference in New Issue
Block a user