grammar update + parsing of new grammar actions + new checks for actions
This commit is contained in:
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user