grammar update + parsing of new grammar actions + new checks for actions

This commit is contained in:
ChUrl
2020-12-15 16:23:25 +01:00
parent 33761016d5
commit e6f2774ed4

View File

@ -1,16 +1,26 @@
package parser.grammar; package parser.grammar;
import parser.ast.ASTNode;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Arrays; import java.util.Arrays;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static parser.grammar.GrammarActions.COMPACT; import static parser.grammar.GrammarAction.DELCHILD;
import static parser.grammar.GrammarActions.NULLABLE; import static parser.grammar.GrammarAction.DELIFEMPTY;
import static parser.grammar.GrammarAction.NAMETOVAL;
import static parser.grammar.GrammarAction.PROMOTE;
import static parser.grammar.GrammarAction.RENAMETO;
import static parser.grammar.GrammarAction.VALTOVAL;
import static parser.grammar.GrammarAction.values;
import static util.Logger.log; import static util.Logger.log;
public class Grammar { public class Grammar {
@ -21,21 +31,31 @@ public class Grammar {
private final String epsilonSymbol; private final String epsilonSymbol;
// Actions // Actions
private final Set<String> compact; private final Map<GrammarAction, Set<String>> actions;
private final Set<String> nullable; private final Map<String, String> renameMappings;
private final Map<String, List<String>> nameToValMappings;
private final Map<String, List<String>> valToValMappings;
private final Map<String, List<String>> delChildMappings;
private final Set<GrammarRule> rules; private final Set<GrammarRule> rules;
public Grammar(Set<String> terminals, Set<String> nonterminals, public Grammar(Set<String> terminals, Set<String> nonterminals,
String startSymbol, String epsilonSymbol, String startSymbol, String epsilonSymbol,
Set<String> compact, Set<String> nullable, Map<GrammarAction, Set<String>> actions,
Map<String, String> renameMappings,
Map<String, List<String>> nameToValMappings,
Map<String, List<String>> valToValMappings,
Map<String, List<String>> delChildMappings,
Set<GrammarRule> rules) { Set<GrammarRule> rules) {
this.terminals = terminals; this.terminals = terminals;
this.nonterminals = nonterminals; this.nonterminals = nonterminals;
this.startSymbol = startSymbol; this.startSymbol = startSymbol;
this.epsilonSymbol = epsilonSymbol; this.epsilonSymbol = epsilonSymbol;
this.compact = compact; this.actions = actions;
this.nullable = nullable; this.renameMappings = renameMappings;
this.nameToValMappings = nameToValMappings;
this.valToValMappings = valToValMappings;
this.delChildMappings = delChildMappings;
this.rules = rules; this.rules = rules;
} }
@ -48,20 +68,35 @@ public class Grammar {
.collect(Collectors.toUnmodifiableList()); .collect(Collectors.toUnmodifiableList());
try { try {
// Grammar
String startSymbol = ""; String startSymbol = "";
String epsilonSymbol = ""; String epsilonSymbol = "";
Set<String> terminals = new HashSet<>(); final Set<String> terminals = new HashSet<>();
Set<String> nonterminals = new HashSet<>(); final Set<String> nonterminals = new HashSet<>();
final Set<GrammarRule> rules = new HashSet<>();
Set<String> compact = new HashSet<>(); // Actions
Set<String> nullable = new HashSet<>(); final Map<GrammarAction, Set<String>> actions = new EnumMap<>(GrammarAction.class);
Set<GrammarRule> rules = new HashSet<>(); final Map<String, String> renameMappings = new HashMap<>();
final Map<String, List<String>> nameToValMappings = new HashMap<>();
final Map<String, List<String>> valToValMappings = new HashMap<>();
final Map<String, List<String>> delChildMappings = new HashMap<>();
for (GrammarAction action : GrammarAction.values()) {
actions.put(action, new HashSet<>());
}
// Init for validity check
final Set<String> actionSet = Arrays.stream(values())
.map(Enum::toString)
.collect(Collectors.toUnmodifiableSet());
log("Parsing Grammar from File:"); log("Parsing Grammar from File:");
for (String line : lines) { for (String line : lines) {
log("Parsed: " + line); log("Parsed: " + line);
// Parse Keywords
if (line.startsWith("START:")) { if (line.startsWith("START:")) {
startSymbol = line.split(" ")[1]; startSymbol = line.split(" ")[1];
@ -75,67 +110,83 @@ public class Grammar {
nonterminals.addAll(Arrays.stream(line.split(" ")).skip(1).collect(Collectors.toSet())); nonterminals.addAll(Arrays.stream(line.split(" ")).skip(1).collect(Collectors.toSet()));
} else { } else {
// "S[] -> E T2 | EPS" wird zu leftside = "S[]" und rightside = "E T2 | epsilon" // Parse Grammar Rules + Actions
String[] split = line.replaceAll("EPS", epsilonSymbol)
// "S[...] -> E T2 | EPS" wird zu leftside = "S[...]" und rightside = "E T2 | eps"
final String[] split = line.replaceAll("EPS", epsilonSymbol)
.split("->"); .split("->");
String leftside = split[0].trim(); String leftside = split[0].trim();
String rightside = split[1].trim(); final String rightside = split[1].trim();
if (leftside.indexOf('[') >= 0) { if (leftside.indexOf('[') >= 0 && leftside.indexOf(']') >= 0) {
// Handle actions if they exist // Handle actions if they are given
int open = leftside.indexOf('['); final int open = leftside.indexOf('[');
int close = leftside.indexOf(']'); final int close = leftside.indexOf(']');
// Aus "S[C R]" wird flags = {"C", "R"} extrahiert // Aus "S[C R=...]" wird flags = {"C", "R=..."}
String[] flags = leftside.substring(open + 1, close).split(" "); final String[] flags = leftside.substring(open + 1, close).split(" ");
List<String> flagList = Arrays.stream(flags) final Set<String> flagSet = Arrays.stream(flags)
.map(String::trim) .map(String::trim)
.filter(flag -> !flag.isEmpty()) .filter(flag -> !flag.isEmpty())
.collect(Collectors.toList()); .collect(Collectors.toUnmodifiableSet());
// Check for action validity // Check for action validity
List<String> enumActions = Arrays.stream(GrammarActions.values()) for (String flag : flagSet) {
.map(action -> action.toString().toLowerCase()) if (!actionSet.contains(flag.split("=")[0].toUpperCase())) {
.collect(Collectors.toList()); throw new GrammarParseException("Invalid Action: " + flag);
for (String flag : flagList) {
if (!enumActions.contains(flag)) {
throw new GrammarParseException("Falsche Action in Grammatik");
} }
} }
// "S[C R]" wird zu "S" // "S[C R=...]" wird zu "S"
leftside = leftside.substring(0, open).trim(); leftside = leftside.substring(0, open).trim();
// Register action // Register actions, flagSet = {"C", "R=..."}
if (flagList.contains(COMPACT.toString().toLowerCase())) { for (String flag : flagSet) {
compact.add(leftside.trim()); final String[] flagSplit = flag.split("=");
log("Registered compact: " + leftside.trim()); final GrammarAction action = GrammarAction.valueOf(flagSplit[0].toUpperCase());
actions.get(action).add(leftside.trim());
log("Registered " + flag + ": " + leftside.trim());
if (flagSplit.length > 1) {
// Handle Action with arguments
// "R=A,B,C" -> argSplit = {"A", "B", "C"}
final int argStart = flag.indexOf('=');
final String[] argSplit = flag.substring(argStart + 1).split(",");
switch (action) {
case DELCHILD -> delChildMappings.put(leftside, Arrays.asList(argSplit));
case VALTOVAL -> valToValMappings.put(leftside, Arrays.asList(argSplit));
case NAMETOVAL -> nameToValMappings.put(leftside, Arrays.asList(argSplit));
case RENAMETO -> renameMappings.put(leftside, argSplit[0]);
}
} }
if (flagList.contains(NULLABLE.toString().toLowerCase())) {
nullable.add(leftside.trim());
log("Registered nullable: " + leftside.trim());
} }
} }
// "E T2 | epsilon" wird zu prods[0] = "E T2" und prods[1] = "epsilon" // "E T2 | epsilon" wird zu prods[0] = "E T2" und prods[1] = "epsilon"
String[] prods = rightside.split("\\|"); final String[] prods = rightside.split("\\|");
for (String prod : prods) { for (String prod : prods) {
GrammarRule rule = new GrammarRule(leftside, prod.split(" ")); final GrammarRule rule = new GrammarRule(leftside, prod.split(" "));
rules.add(rule); rules.add(rule);
} }
} }
} }
log("\n" + compact); log("\n" + actions);
log("-".repeat(100)); log("-".repeat(100));
return new Grammar(terminals, nonterminals, return new Grammar(terminals, nonterminals,
startSymbol, epsilonSymbol, startSymbol, epsilonSymbol,
compact, nullable, actions,
renameMappings,
nameToValMappings,
valToValMappings,
delChildMappings,
rules); rules);
} catch (Exception e) { } catch (Exception e) {
log("Die Grammatik kann nicht gelesen werden!"); log("Die Grammatik kann nicht gelesen werden!");
@ -179,11 +230,90 @@ public class Grammar {
.collect(Collectors.toUnmodifiableSet()); .collect(Collectors.toUnmodifiableSet());
} }
public boolean hasCompact(String leftside) { // Actions ---------------------------------------------------------------------------------------------------------
return this.compact != null && this.compact.contains(leftside);
/**
* Es wird nicht root promoted, sondern roots einziges Kind.
* Checkt auch auf Anzahl der Kinder.
*/
public boolean canPromoteChild(ASTNode root) {
return this.canPromoteChild(root.getName())
&& root.getChildren().size() == 1
&& root.getValue().isEmpty();
} }
public boolean hasNullable(String leftside) { private boolean canPromoteChild(String sym) {
return this.nullable != null && this.nullable.contains(leftside); return this.actions.get(PROMOTE).contains(sym);
}
/**
* Checkt auch auf Anzahl der Kinder und vorhandene Value.
*/
public boolean canDeleteIfEmpty(ASTNode root) {
return this.canDeleteIfEmpty(root.getName())
&& root.getValue().isEmpty()
&& !root.hasChildren();
}
public boolean canDeleteIfEmpty(String sym) {
return this.actions.get(DELIFEMPTY).contains(sym);
}
/**
* Checkt auch auf Anzahl der Kinder.
* Epsilon-Knoten werden immer gelöscht.
*/
public boolean canDeleteChild(ASTNode parent, ASTNode child) {
return this.canDeleteChild(parent.getName(), child.getName())
&& !child.hasChildren();
}
public boolean canDeleteChild(String parent, String child) {
return (this.actions.get(DELCHILD).contains(parent)
&& this.delChildMappings.get(parent).contains(child))
|| (child.equals(this.epsilonSymbol));
}
public boolean canBeRenamed(ASTNode root) {
return this.canBeRenamed(root.getName());
}
public boolean canBeRenamed(String sym) {
return this.actions.get(RENAMETO).contains(sym);
}
public String getNewName(ASTNode root) {
return this.getNewName(root.getName());
}
public String getNewName(String sym) {
return this.renameMappings.get(sym);
}
public boolean hasValToVal(ASTNode parent, ASTNode child) {
return this.hasValToVal(parent.getName(), child.getName());
}
public boolean hasValToVal(String parent, String child) {
return this.actions.get(VALTOVAL).contains(parent)
&& this.valToValMappings.get(parent).contains(child);
}
/**
* Checkt auch auf bereits existierende Values.
*/
public boolean canMoveNameToVal(ASTNode parent, ASTNode child) {
return this.canMoveNameToVal(parent.getName(), child.getName())
&& parent.getValue().isEmpty();
}
public boolean canMoveNameToVal(String parent, String child) {
return this.actions.get(NAMETOVAL).contains(parent)
&& this.nameToValMappings.get(parent).contains(child);
} }
} }