grammar update + complete compacter overhaul with new grammar actions
This commit is contained in:
@ -2,9 +2,9 @@ package parser.ast;
|
||||
|
||||
import parser.grammar.Grammar;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static util.Logger.log;
|
||||
|
||||
@ -12,268 +12,162 @@ public final class ASTCompacter {
|
||||
|
||||
private ASTCompacter() {}
|
||||
|
||||
// Grundreinigung
|
||||
public static void clean(AST tree, Grammar grammar) {
|
||||
int removed = 0;
|
||||
deleteChildren(tree, grammar);
|
||||
deleteIfEmpty(tree, grammar);
|
||||
promote(tree, grammar);
|
||||
|
||||
int change;
|
||||
do {
|
||||
change = compact(tree, grammar)
|
||||
+ removeEpsilon(tree, grammar)
|
||||
+ removeRedundant(tree)
|
||||
+ removeNullable(tree, grammar);
|
||||
renameTo(tree, grammar);
|
||||
nameToValue(tree, grammar);
|
||||
valueToValue(tree, grammar);
|
||||
|
||||
removed += change;
|
||||
} while (change != 0);
|
||||
|
||||
renameEXPR(tree);
|
||||
moveOperatorToEXPR(tree);
|
||||
moveIdentifierToASSIGNMENT(tree);
|
||||
|
||||
log(tree.toString());
|
||||
log("\nCleaned Tree: " + removed + " nodes removed.");
|
||||
log("\nCleaned Tree:\n" + tree);
|
||||
log("-".repeat(100));
|
||||
|
||||
System.out.println("- Tree compression successful.");
|
||||
}
|
||||
|
||||
// Entfernt [compact]-able Nodes (Reicht Werte nach oben)
|
||||
public static int compact(AST tree, Grammar grammar) {
|
||||
int compacted = 0;
|
||||
|
||||
int changed;
|
||||
do {
|
||||
changed = compact(tree.getRoot(), grammar);
|
||||
compacted += changed;
|
||||
} while (changed != 0);
|
||||
|
||||
if (compacted != 0) {
|
||||
log("Flattened " + compacted + " nodes.");
|
||||
log("-".repeat(100));
|
||||
}
|
||||
|
||||
return compacted;
|
||||
// Entfernt [promote]-able Nodes (Reicht Werte nach oben)
|
||||
public static void promote(AST tree, Grammar grammar) {
|
||||
log("\nPromoting nodes:");
|
||||
promote(tree.getRoot(), grammar);
|
||||
}
|
||||
|
||||
private static int compact(ASTNode root, Grammar grammar) {
|
||||
int compacted = 0;
|
||||
Collection<ASTNode> toRemove = new HashSet<>();
|
||||
private static void promote(ASTNode root, Grammar grammar) {
|
||||
final Collection<ASTNode> toRemove = new HashSet<>();
|
||||
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
if (grammar.hasCompact(root.getName())
|
||||
&& root.getChildren().size() == 1) {
|
||||
promote(child, grammar);
|
||||
|
||||
log("Compacting " + root.getName() + " -> " + child.getName());
|
||||
|
||||
root.setName(child.getName());
|
||||
root.setValue(child.getValue());
|
||||
root.setChildren(child.getChildren());
|
||||
|
||||
toRemove.add(child);
|
||||
|
||||
} else {
|
||||
compacted += compact(child, grammar);
|
||||
// Impliziert, dass die for-schleife nur 1x läuft, deshalb ist child das richtige Kind
|
||||
if (!grammar.canPromoteChild(root)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log("Promoting " + child.getName() + " -> " + root.getName());
|
||||
|
||||
root.setName(child.getName());
|
||||
root.setValue(child.getValue());
|
||||
root.setChildren(child.getChildren());
|
||||
toRemove.add(child);
|
||||
}
|
||||
|
||||
root.getChildren().removeAll(toRemove);
|
||||
|
||||
return compacted + toRemove.size();
|
||||
}
|
||||
|
||||
// Entfernt [nullable] Nodes (löscht Nodes ohne Inhalt)
|
||||
public static int removeNullable(AST tree, Grammar grammar) {
|
||||
int removed = removeNullable(tree.getRoot(), grammar);
|
||||
|
||||
if (removed != 0) {
|
||||
log("Removed " + removed + " nodes.");
|
||||
log("-".repeat(100));
|
||||
}
|
||||
|
||||
return removed;
|
||||
// Entfernt [delIfEmpty] Nodes (löscht Nodes ohne Inhalt)
|
||||
public static void deleteIfEmpty(AST tree, Grammar grammar) {
|
||||
log("\nDeleting empty nodes:");
|
||||
deleteIfEmpty(tree.getRoot(), grammar);
|
||||
}
|
||||
|
||||
private static int removeNullable(ASTNode root, Grammar grammar) {
|
||||
int removed = 0;
|
||||
Collection<ASTNode> toRemove = new HashSet<>();
|
||||
private static void deleteIfEmpty(ASTNode root, Grammar grammar) {
|
||||
final Collection<ASTNode> toRemove = new HashSet<>();
|
||||
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
if (grammar.hasNullable(child.getName())
|
||||
&& child.getValue().isEmpty()
|
||||
&& !child.hasChildren()) {
|
||||
deleteIfEmpty(child, grammar);
|
||||
|
||||
log("Removing " + child.getName());
|
||||
|
||||
toRemove.add(child);
|
||||
} else {
|
||||
removed += removeNullable(child, grammar);
|
||||
if (!grammar.canDeleteIfEmpty(child)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log("Removing " + child.getName());
|
||||
|
||||
toRemove.add(child);
|
||||
}
|
||||
|
||||
root.getChildren().removeAll(toRemove);
|
||||
|
||||
return removed + toRemove.size();
|
||||
}
|
||||
|
||||
// Löscht epsilon-Nodes
|
||||
public static int removeEpsilon(AST tree, Grammar grammar) {
|
||||
int removed = removeEpsilon(tree.getRoot(), grammar);
|
||||
|
||||
if (removed != 0) {
|
||||
log("Removed " + removed + " nodes.");
|
||||
log("-".repeat(100));
|
||||
}
|
||||
|
||||
return removed;
|
||||
// Löscht redundante Informationen in [delChildren]-Nodes (z.b. IF-child von COND) und Epsilon-Nodes
|
||||
public static void deleteChildren(AST tree, Grammar grammar) {
|
||||
log("Removing redundant children:");
|
||||
deleteChildren(tree.getRoot(), grammar);
|
||||
}
|
||||
|
||||
private static int removeEpsilon(ASTNode root, Grammar grammar) {
|
||||
int removed = 0;
|
||||
Collection<ASTNode> toRemove = new HashSet<>();
|
||||
private static void deleteChildren(ASTNode root, Grammar grammar) {
|
||||
final Collection<ASTNode> toRemove = new HashSet<>();
|
||||
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
if (child.getName().equals(grammar.getEpsilonSymbol()) && !child.hasChildren()) {
|
||||
log("Removing " + root.getName() + " -> " + child.getName());
|
||||
toRemove.add(child);
|
||||
} else {
|
||||
removed += removeEpsilon(child, grammar);
|
||||
deleteChildren(child, grammar);
|
||||
|
||||
if (!grammar.canDeleteChild(root, child)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log("Removing " + root.getName() + " -> " + child.getName());
|
||||
|
||||
toRemove.add(child);
|
||||
}
|
||||
|
||||
root.getChildren().removeAll(toRemove);
|
||||
|
||||
return removed + toRemove.size();
|
||||
}
|
||||
|
||||
// Löscht doppelte Informationen (z.b. IF-child von COND)
|
||||
public static int removeRedundant(AST tree) {
|
||||
int removed = removeRedundant(tree.getRoot());
|
||||
|
||||
if (removed != 0) {
|
||||
log("Removed " + removed + " nodes.");
|
||||
log("-".repeat(100));
|
||||
}
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
private static int removeRedundant(ASTNode root) {
|
||||
int removed = 0;
|
||||
Collection<ASTNode> toRemove = new HashSet<>();
|
||||
|
||||
Collection<String> removable = Arrays.asList("IF", "ELSE", "WHILE", "ASSIGN");
|
||||
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
if (removable.contains(child.getName()) && !child.hasChildren()) {
|
||||
log("Removing " + root.getName() + " -> " + child.getName());
|
||||
toRemove.add(child);
|
||||
} else {
|
||||
removed += removeRedundant(child);
|
||||
}
|
||||
}
|
||||
|
||||
root.getChildren().removeAll(toRemove);
|
||||
|
||||
return removed + toRemove.size();
|
||||
}
|
||||
|
||||
// Umbenennungen
|
||||
// EXPR_2 -> EXPR, EXPR_F -> EXPR
|
||||
private static void renameEXPR(AST tree) {
|
||||
renameEXPR(tree.getRoot());
|
||||
log("-".repeat(100));
|
||||
private static void renameTo(AST tree, Grammar grammar) {
|
||||
log("\nRenaming nodes:");
|
||||
renameTo(tree.getRoot(), grammar);
|
||||
}
|
||||
|
||||
private static void renameEXPR(ASTNode root) {
|
||||
String newName = switch (root.getName()) {
|
||||
case "EXPR_2", "EXPR_F" -> "EXPR";
|
||||
default -> root.getName();
|
||||
};
|
||||
|
||||
if (!newName.equals(root.getName())) {
|
||||
log("Rename " + root.getName() + " to " + newName + ".");
|
||||
}
|
||||
|
||||
root.setName(newName);
|
||||
|
||||
private static void renameTo(ASTNode root, Grammar grammar) {
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
renameEXPR(child);
|
||||
}
|
||||
}
|
||||
renameTo(child, grammar);
|
||||
|
||||
// TODO: Move Regeln zusammenfassen mit actions?
|
||||
// EXPR bekommt die Operation als Value anstatt als Child
|
||||
public static void moveOperatorToEXPR(AST tree) {
|
||||
moveOperatorToEXPR(tree.getRoot());
|
||||
log("-".repeat(100));
|
||||
}
|
||||
|
||||
private static void moveOperatorToEXPR(ASTNode root) {
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
moveOperatorToEXPR(child);
|
||||
}
|
||||
|
||||
ASTNode op = getOp(root);
|
||||
|
||||
if (op == null || !"EXPR".equals(root.getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
log("Moving operator " + op.getName() + " to " + root.getName());
|
||||
root.setValue(op.getName());
|
||||
root.getChildren().remove(op);
|
||||
}
|
||||
|
||||
// Jede Expression hat maximal 1x Operator
|
||||
private static ASTNode getOp(ASTNode root) {
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
ASTNode op = switch (child.getName()) {
|
||||
case "ADD", "SUB", "MUL", "DIV", "MOD" -> child;
|
||||
case "NOT", "AND", "OR" -> child;
|
||||
case "LESS", "LESS_EQUAL", "GREATER", "GREATER_EQUAL", "EQUAL", "NOT_EQUAL" -> child;
|
||||
default -> null;
|
||||
};
|
||||
|
||||
if (op != null) {
|
||||
return op;
|
||||
if (!grammar.canBeRenamed(root)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log("Rename " + root.getName() + " to " + grammar.getNewName(root) + ".");
|
||||
|
||||
root.setName(grammar.getNewName(root));
|
||||
}
|
||||
}
|
||||
|
||||
public static void nameToValue(AST tree, Grammar grammar) {
|
||||
log("\nMoving names to values:");
|
||||
nameToValue(tree.getRoot(), grammar);
|
||||
}
|
||||
|
||||
private static void nameToValue(ASTNode root, Grammar grammar) {
|
||||
final Collection<ASTNode> toRemove = new HashSet<>();
|
||||
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
nameToValue(child, grammar);
|
||||
|
||||
if (!grammar.canMoveNameToVal(root, child)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log("Moving " + child.getName() + " to value of " + root.getName());
|
||||
|
||||
root.setValue(child.getName());
|
||||
toRemove.add(child);
|
||||
}
|
||||
|
||||
return null;
|
||||
root.getChildren().removeAll(toRemove);
|
||||
}
|
||||
|
||||
// Assignment bekommt den Identifier als Value anstatt als Child
|
||||
public static void moveIdentifierToASSIGNMENT(AST tree) {
|
||||
moveIdentifierToASSIGNMENT(tree.getRoot());
|
||||
log("-".repeat(100));
|
||||
public static void valueToValue(AST tree, Grammar grammar) {
|
||||
log("\nMoving values to values:");
|
||||
valueToValue(tree.getRoot(), grammar);
|
||||
}
|
||||
|
||||
private static void moveIdentifierToASSIGNMENT(ASTNode root) {
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
moveIdentifierToASSIGNMENT(child);
|
||||
}
|
||||
|
||||
ASTNode id = getId(root);
|
||||
|
||||
if (id == null || !"ASSIGNMENT".equals(root.getName())) {
|
||||
return;
|
||||
}
|
||||
|
||||
log("Moving identifier " + id.getValue() + " to " + root.getName());
|
||||
root.setValue(id.getValue());
|
||||
root.getChildren().remove(id);
|
||||
}
|
||||
|
||||
// Assignments koennen >1 Identifier haben: Wenn ja, nimm den 2.
|
||||
private static ASTNode getId(ASTNode root) {
|
||||
ASTNode childOut = null;
|
||||
private static void valueToValue(ASTNode root, Grammar grammar) {
|
||||
final Set<ASTNode> toRemove = new HashSet<>();
|
||||
|
||||
for (ASTNode child : root.getChildren()) {
|
||||
if ("IDENTIFIER".equals(child.getName())) {
|
||||
childOut = child;
|
||||
valueToValue(child, grammar);
|
||||
|
||||
if (!grammar.hasValToVal(root, child)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
log("Moving " + child.getValue() + " to value of " + root.getName());
|
||||
|
||||
root.setValue(child.getValue());
|
||||
toRemove.add(child);
|
||||
}
|
||||
|
||||
return childOut;
|
||||
root.getChildren().removeAll(toRemove);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user