rework Grammar
This commit is contained in:
@ -28,10 +28,6 @@ public class ParsingTable {
|
|||||||
return this.parsetable.get(new SimpleEntry<>(nonterminal, terminal));
|
return this.parsetable.get(new SimpleEntry<>(nonterminal, terminal));
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStartSymbol() {
|
|
||||||
return this.grammar.getStartSymbol();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<String> getNonterminals() {
|
public Set<String> getNonterminals() {
|
||||||
return this.grammar.getNonterminals();
|
return this.grammar.getNonterminals();
|
||||||
}
|
}
|
||||||
@ -40,10 +36,6 @@ public class ParsingTable {
|
|||||||
return this.grammar.getTerminals();
|
return this.grammar.getTerminals();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getEpsilon() {
|
|
||||||
return this.grammar.getEpsilonSymbol();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
final StringBuilder output = new StringBuilder();
|
final StringBuilder output = new StringBuilder();
|
||||||
|
@ -39,7 +39,7 @@ public class StupsParser {
|
|||||||
|
|
||||||
public SyntaxTree parse(List<? extends Token> token, Vocabulary voc) {
|
public SyntaxTree parse(List<? extends Token> token, Vocabulary voc) {
|
||||||
System.out.println(" - Parsing program...");
|
System.out.println(" - Parsing program...");
|
||||||
final SyntaxTreeNode root = new SyntaxTreeNode(this.parsetable.getStartSymbol(), 0);
|
final SyntaxTreeNode root = new SyntaxTreeNode(Grammar.START_SYMBOL, 0);
|
||||||
final SyntaxTree tree = new SyntaxTree(root);
|
final SyntaxTree tree = new SyntaxTree(root);
|
||||||
final Deque<SyntaxTreeNode> stack = new ArrayDeque<>();
|
final Deque<SyntaxTreeNode> stack = new ArrayDeque<>();
|
||||||
stack.push(root);
|
stack.push(root);
|
||||||
@ -69,7 +69,7 @@ public class StupsParser {
|
|||||||
|
|
||||||
final String prod = this.parsetable.get(top, currentTokenSym);
|
final String prod = this.parsetable.get(top, currentTokenSym);
|
||||||
|
|
||||||
if (top.equals(this.parsetable.getEpsilon())) {
|
if (top.equals(Grammar.EPSILON_SYMBOL)) {
|
||||||
// Wenn auf dem Stack das Epsilonsymbol liegt
|
// Wenn auf dem Stack das Epsilonsymbol liegt
|
||||||
|
|
||||||
stack.pop();
|
stack.pop();
|
||||||
|
@ -6,6 +6,7 @@ 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.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
@ -13,7 +14,6 @@ import java.util.HashSet;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Pattern;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import static parser.grammar.GrammarAction.DELCHILD;
|
import static parser.grammar.GrammarAction.DELCHILD;
|
||||||
@ -22,30 +22,56 @@ import static parser.grammar.GrammarAction.NAMETOVAL;
|
|||||||
import static parser.grammar.GrammarAction.PROMOTE;
|
import static parser.grammar.GrammarAction.PROMOTE;
|
||||||
import static parser.grammar.GrammarAction.RENAMETO;
|
import static parser.grammar.GrammarAction.RENAMETO;
|
||||||
import static parser.grammar.GrammarAction.VALTOVAL;
|
import static parser.grammar.GrammarAction.VALTOVAL;
|
||||||
import static parser.grammar.GrammarAction.values;
|
|
||||||
import static util.Logger.log;
|
import static util.Logger.log;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repräsentiert die Parse-Grammatik und die Kontextaktionen.
|
||||||
|
*/
|
||||||
public class Grammar {
|
public class Grammar {
|
||||||
|
|
||||||
private static final Pattern EPS = Pattern.compile("EPS");
|
// Grammar
|
||||||
private static final Pattern ARROW = Pattern.compile("->");
|
public static final String START_SYMBOL = "S";
|
||||||
|
public static final String EPSILON_SYMBOL = "eps";
|
||||||
|
|
||||||
private final Set<String> terminals;
|
private final Set<String> terminals;
|
||||||
private final Set<String> nonterminals;
|
private final Set<String> nonterminals;
|
||||||
private final String startSymbol;
|
|
||||||
private final String epsilonSymbol;
|
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
private final Map<GrammarAction, Set<String>> actions;
|
|
||||||
|
/**
|
||||||
|
* Jeder Kontextaktion werden alle leftsides zugewiesen, welche diese Aktion ausführen.
|
||||||
|
*/
|
||||||
|
private final Map<GrammarAction, Set<String>> actionMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jeder leftside mit [renameto=name] wird der entsprechende neue Name zugewiesen.
|
||||||
|
*/
|
||||||
private final Map<String, String> renameMappings;
|
private final Map<String, String> renameMappings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jeder leftside mit [nametoval=children] werden die entpsrechenden Children zugewiesen,
|
||||||
|
* deren Namen in die Parentvalue gschoben werden.
|
||||||
|
*/
|
||||||
private final Map<String, List<String>> nameToValMappings;
|
private final Map<String, List<String>> nameToValMappings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jeder leftside mit [valtoval=children] werden die entpsrechenden Children zugewiesen,
|
||||||
|
* deren Values in die Parentvalue gschoben werden.
|
||||||
|
*/
|
||||||
private final Map<String, List<String>> valToValMappings;
|
private final Map<String, List<String>> valToValMappings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Jeder Leftside mit [delchild=children] werden die entpsrechenden Children zugewiesen, welche entfernt werden.
|
||||||
|
*/
|
||||||
private final Map<String, List<String>> delChildMappings;
|
private final Map<String, List<String>> delChildMappings;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Die eigentlichen Produktionsregeln der Form leftside -> rightside.
|
||||||
|
*/
|
||||||
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,
|
Map<GrammarAction, Set<String>> actionMap,
|
||||||
Map<GrammarAction, Set<String>> actions,
|
|
||||||
Map<String, String> renameMappings,
|
Map<String, String> renameMappings,
|
||||||
Map<String, List<String>> nameToValMappings,
|
Map<String, List<String>> nameToValMappings,
|
||||||
Map<String, List<String>> valToValMappings,
|
Map<String, List<String>> valToValMappings,
|
||||||
@ -55,10 +81,8 @@ public class Grammar {
|
|||||||
this.terminals = Collections.unmodifiableSet(terminals);
|
this.terminals = Collections.unmodifiableSet(terminals);
|
||||||
this.nonterminals = Collections.unmodifiableSet(nonterminals);
|
this.nonterminals = Collections.unmodifiableSet(nonterminals);
|
||||||
this.rules = Collections.unmodifiableSet(rules);
|
this.rules = Collections.unmodifiableSet(rules);
|
||||||
this.startSymbol = startSymbol;
|
|
||||||
this.epsilonSymbol = epsilonSymbol;
|
|
||||||
|
|
||||||
this.actions = Collections.unmodifiableMap(actions);
|
this.actionMap = Collections.unmodifiableMap(actionMap);
|
||||||
this.renameMappings = Collections.unmodifiableMap(renameMappings);
|
this.renameMappings = Collections.unmodifiableMap(renameMappings);
|
||||||
this.nameToValMappings = Collections.unmodifiableMap(nameToValMappings);
|
this.nameToValMappings = Collections.unmodifiableMap(nameToValMappings);
|
||||||
this.valToValMappings = Collections.unmodifiableMap(valToValMappings);
|
this.valToValMappings = Collections.unmodifiableMap(valToValMappings);
|
||||||
@ -69,141 +93,197 @@ public class Grammar {
|
|||||||
System.out.println(" - Reading parser-grammar...");
|
System.out.println(" - Reading parser-grammar...");
|
||||||
List<String> lines = Files.readAllLines(path);
|
List<String> lines = Files.readAllLines(path);
|
||||||
|
|
||||||
|
// Remove Whitespace + Comments
|
||||||
lines = lines.stream()
|
lines = lines.stream()
|
||||||
.map(String::trim)
|
.map(String::trim)
|
||||||
.filter(line -> !(line.isBlank() || line.startsWith("//")))
|
.filter(line -> !(line.isBlank() || line.startsWith("//")))
|
||||||
.collect(Collectors.toUnmodifiableList());
|
.collect(Collectors.toUnmodifiableList());
|
||||||
|
|
||||||
try {
|
// Grammar
|
||||||
// Grammar
|
final Set<String> terminals = new HashSet<>();
|
||||||
String startSymbol = "";
|
final Set<String> nonterminals = new HashSet<>();
|
||||||
String epsilonSymbol = "";
|
final Set<GrammarRule> rules = new HashSet<>();
|
||||||
final Set<String> terminals = new HashSet<>();
|
|
||||||
final Set<String> nonterminals = new HashSet<>();
|
|
||||||
final Set<GrammarRule> rules = new HashSet<>();
|
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
final Map<GrammarAction, Set<String>> actions = new EnumMap<>(GrammarAction.class);
|
final Map<GrammarAction, Set<String>> actionMap = new EnumMap<>(GrammarAction.class);
|
||||||
final Map<String, String> renameMappings = new HashMap<>();
|
final Map<String, String> renameMappings = new HashMap<>();
|
||||||
final Map<String, List<String>> nameToValMappings = new HashMap<>();
|
final Map<String, List<String>> nameToValMappings = new HashMap<>();
|
||||||
final Map<String, List<String>> valToValMappings = new HashMap<>();
|
final Map<String, List<String>> valToValMappings = new HashMap<>();
|
||||||
final Map<String, List<String>> delChildMappings = new HashMap<>();
|
final Map<String, List<String>> delChildMappings = new HashMap<>();
|
||||||
|
|
||||||
for (GrammarAction action : values()) {
|
// Init actionMap
|
||||||
actions.put(action, new HashSet<>());
|
for (GrammarAction action : GrammarAction.values()) {
|
||||||
}
|
actionMap.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:");
|
|
||||||
for (String line : lines) {
|
|
||||||
|
|
||||||
log("Parsed: " + line);
|
|
||||||
|
|
||||||
// Parse Keywords
|
|
||||||
if (line.startsWith("START:")) {
|
|
||||||
|
|
||||||
startSymbol = line.split(" ")[1];
|
|
||||||
} else if (line.startsWith("EPS:")) {
|
|
||||||
|
|
||||||
epsilonSymbol = line.split(" ")[1];
|
|
||||||
} else if (line.startsWith("TERM:")) {
|
|
||||||
|
|
||||||
terminals.addAll(Arrays.stream(line.split(" ")).skip(1).collect(Collectors.toSet()));
|
|
||||||
} else if (line.startsWith("NTERM:")) {
|
|
||||||
|
|
||||||
nonterminals.addAll(Arrays.stream(line.split(" ")).skip(1).collect(Collectors.toSet()));
|
|
||||||
} else {
|
|
||||||
// Parse Grammar Rules + Actions
|
|
||||||
|
|
||||||
// "S[...] -> E T2 | EPS" wird zu leftside = "S[...]" und rightside = "E T2 | eps"
|
|
||||||
final String[] split = ARROW.split(EPS.matcher(line).replaceAll(epsilonSymbol));
|
|
||||||
String leftside = split[0].trim();
|
|
||||||
final String rightside = split[1].trim();
|
|
||||||
|
|
||||||
if (leftside.indexOf('[') >= 0 && leftside.indexOf(']') >= 0) {
|
|
||||||
// Handle actions if they are given
|
|
||||||
|
|
||||||
final int open = leftside.indexOf('[');
|
|
||||||
final int close = leftside.indexOf(']');
|
|
||||||
|
|
||||||
// Aus "S[C R=...]" wird flags = {"C", "R=..."}
|
|
||||||
final String[] flags = leftside.substring(open + 1, close).split(" ");
|
|
||||||
final Set<String> flagSet = Arrays.stream(flags)
|
|
||||||
.map(String::trim)
|
|
||||||
.filter(flag -> !flag.isEmpty())
|
|
||||||
.collect(Collectors.toUnmodifiableSet());
|
|
||||||
|
|
||||||
// Check for action validity
|
|
||||||
for (String flag : flagSet) {
|
|
||||||
if (!actionSet.contains(flag.split("=")[0].toUpperCase())) {
|
|
||||||
throw new GrammarParseException("Invalid Action: " + flag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// "S[C R=...]" wird zu "S"
|
|
||||||
leftside = leftside.substring(0, open).trim();
|
|
||||||
|
|
||||||
// Register actions, flagSet = {"C", "R=..."}
|
|
||||||
for (String flag : flagSet) {
|
|
||||||
final String[] flagSplit = flag.split("=");
|
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// "E T2 | epsilon" wird zu prods[0] = "E T2" und prods[1] = "epsilon"
|
|
||||||
final String[] prods = rightside.split("\\|");
|
|
||||||
|
|
||||||
for (String prod : prods) {
|
|
||||||
final GrammarRule rule = new GrammarRule(leftside, prod.split(" "));
|
|
||||||
rules.add(rule);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
log("\n" + actions);
|
|
||||||
log("-".repeat(100));
|
|
||||||
System.out.println("Grammar parsed successfully.");
|
|
||||||
|
|
||||||
return new Grammar(terminals, nonterminals,
|
|
||||||
startSymbol, epsilonSymbol,
|
|
||||||
actions,
|
|
||||||
renameMappings,
|
|
||||||
nameToValMappings,
|
|
||||||
valToValMappings,
|
|
||||||
delChildMappings,
|
|
||||||
rules);
|
|
||||||
} catch (Exception e) {
|
|
||||||
log("Die Grammatik kann nicht gelesen werden!");
|
|
||||||
log(path.toString());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
log("Parsing Grammar from File:");
|
||||||
|
for (String currentLine : lines) {
|
||||||
|
|
||||||
|
log("Parsed: " + currentLine);
|
||||||
|
|
||||||
|
// Parse Keywords
|
||||||
|
if (currentLine.startsWith("TERM:")) {
|
||||||
|
|
||||||
|
terminals.addAll(Arrays.stream(currentLine.split(" ")).skip(1).collect(Collectors.toSet()));
|
||||||
|
} else if (currentLine.startsWith("NTERM:")) {
|
||||||
|
|
||||||
|
nonterminals.addAll(Arrays.stream(currentLine.split(" ")).skip(1).collect(Collectors.toSet()));
|
||||||
|
} else {
|
||||||
|
// Parse regular lines
|
||||||
|
|
||||||
|
parseRegularLine(currentLine, actionMap,
|
||||||
|
delChildMappings, valToValMappings, nameToValMappings, renameMappings,
|
||||||
|
rules);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log("\n" + actionMap);
|
||||||
|
log("-".repeat(100));
|
||||||
|
System.out.println("Grammar parsed successfully.");
|
||||||
|
|
||||||
|
return new Grammar(terminals, nonterminals,
|
||||||
|
actionMap, renameMappings, nameToValMappings,
|
||||||
|
valToValMappings, delChildMappings, rules);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Es wird eine normale Zeile der Form leftside[actions] -> rightside geparst.
|
||||||
|
* Die Produktionsregeln sowie die Kontextaktionen werden registriert.
|
||||||
|
*/
|
||||||
|
private static void parseRegularLine(String currentLine,
|
||||||
|
Map<GrammarAction, Set<String>> actions,
|
||||||
|
Map<String, List<String>> delChildMappings,
|
||||||
|
Map<String, List<String>> valToValMappings,
|
||||||
|
Map<String, List<String>> nameToValMappings,
|
||||||
|
Map<String, String> renameMappings,
|
||||||
|
Collection<GrammarRule> rules) {
|
||||||
|
|
||||||
|
// "S[...] -> E T2 | eps" wird zu leftside = "S[...]" und rightside = "E T2 | eps"
|
||||||
|
final String[] split = currentLine.split("->");
|
||||||
|
String leftside = split[0].trim();
|
||||||
|
final String rightside = split[1].trim();
|
||||||
|
|
||||||
|
final int open = leftside.indexOf('[');
|
||||||
|
final int close = leftside.indexOf(']');
|
||||||
|
|
||||||
|
if (open >= 0 && close >= 0) {
|
||||||
|
// Handle actions if they are given
|
||||||
|
|
||||||
|
final Set<String> actionSet = parseActionSet(leftside, open, close);
|
||||||
|
|
||||||
|
// Validate Actions
|
||||||
|
throwOnInvalidActionSet(actionSet);
|
||||||
|
|
||||||
|
// "S[C R=...]" wird zu "S"
|
||||||
|
leftside = leftside.substring(0, open).trim();
|
||||||
|
|
||||||
|
// Register actions, flagSet = {"C", "R=..."}
|
||||||
|
for (String flag : actionSet) {
|
||||||
|
registerAction(flag, leftside, actions,
|
||||||
|
delChildMappings, valToValMappings, nameToValMappings, renameMappings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerProductionRules(leftside, rightside, rules);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Es wird die Menge an Kontextaktionen [action1,action2,...] ermittelt.
|
||||||
|
*/
|
||||||
|
private static Set<String> parseActionSet(String leftside, int open, int close) {
|
||||||
|
// Aus "S[C R=...]" wird flags = {"C", "R=..."}
|
||||||
|
final String[] flags = leftside.substring(open + 1, close).split(" ");
|
||||||
|
|
||||||
|
return Arrays.stream(flags)
|
||||||
|
.map(String::trim)
|
||||||
|
.filter(flag -> !flag.isEmpty())
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Es wird eine beliebige Kontextaktion geparst und der entsprechenden Map hinzugefügt.
|
||||||
|
*/
|
||||||
|
private static void registerAction(String flag, String leftside,
|
||||||
|
Map<GrammarAction, Set<String>> actions,
|
||||||
|
Map<String, List<String>> delChildMappings,
|
||||||
|
Map<String, List<String>> valToValMappings,
|
||||||
|
Map<String, List<String>> nameToValMappings,
|
||||||
|
Map<String, String> renameMappings) {
|
||||||
|
|
||||||
|
final String[] flagSplit = flag.split("=");
|
||||||
|
final GrammarAction action = GrammarAction.valueOf(flagSplit[0].toUpperCase());
|
||||||
|
|
||||||
|
registerRegularAction(action, leftside, flag, actions);
|
||||||
|
|
||||||
|
if (flagSplit.length > 1) {
|
||||||
|
|
||||||
|
registerActionArguments(flag, action, leftside,
|
||||||
|
delChildMappings, valToValMappings, nameToValMappings, renameMappings);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Es wird ein Eintrag in der action-Map mit der entsprechenden leftside hinzugefügt.
|
||||||
|
*/
|
||||||
|
private static void registerRegularAction(GrammarAction action, String leftside, String flag,
|
||||||
|
Map<GrammarAction, Set<String>> actions) {
|
||||||
|
|
||||||
|
actions.get(action).add(leftside.trim());
|
||||||
|
log("Registered " + flag + ": " + leftside.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Es wird eine Kontextaktion der Form [action=arguments] geparst und der entsprechenden Map hinzugefügt.
|
||||||
|
*/
|
||||||
|
private static void registerActionArguments(String flag, GrammarAction action, String leftside,
|
||||||
|
Map<String, List<String>> delChildMappings,
|
||||||
|
Map<String, List<String>> valToValMappings,
|
||||||
|
Map<String, List<String>> nameToValMappings,
|
||||||
|
Map<String, String> renameMappings) {
|
||||||
|
|
||||||
|
// "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]);
|
||||||
|
default -> throw new GrammarParseException("Unexpected value for arguments: " + action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Der Regelmenge wird eine neue Regel der Form leftside -> rightside hinzugefügt.
|
||||||
|
* Ist rightside dabei verodert, also leftside -> right1 | right2 | right3, dann
|
||||||
|
* wird rightside gesplittet.
|
||||||
|
*/
|
||||||
|
private static void registerProductionRules(String leftside, String rightside, Collection<GrammarRule> rules) {
|
||||||
|
// "E T2 | epsilon" wird zu prods[0] = "E T2" und prods[1] = "epsilon"
|
||||||
|
final String[] prods = rightside.split("\\|");
|
||||||
|
|
||||||
|
for (String prod : prods) {
|
||||||
|
final GrammarRule rule = new GrammarRule(leftside, prod.split(" "));
|
||||||
|
rules.add(rule);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void throwOnInvalidActionSet(Iterable<String> flagSet) {
|
||||||
|
final Set<String> actionSet = Arrays.stream(GrammarAction.values())
|
||||||
|
.map(Enum::toString)
|
||||||
|
.collect(Collectors.toUnmodifiableSet());
|
||||||
|
|
||||||
|
for (String flag : flagSet) {
|
||||||
|
if (!actionSet.contains(flag.split("=")[0].toUpperCase())) {
|
||||||
|
throw new GrammarParseException("Invalid Action: " + flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Getters
|
||||||
|
|
||||||
public Set<String> getTerminals() {
|
public Set<String> getTerminals() {
|
||||||
return this.terminals;
|
return this.terminals;
|
||||||
}
|
}
|
||||||
@ -212,18 +292,13 @@ public class Grammar {
|
|||||||
return this.nonterminals;
|
return this.nonterminals;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getStartSymbol() {
|
|
||||||
return this.startSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEpsilonSymbol() {
|
|
||||||
return this.epsilonSymbol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<GrammarRule> getRules() {
|
public Set<GrammarRule> getRules() {
|
||||||
return this.rules;
|
return this.rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ermittelt alle möglichen Produktionen, welche zu einer leftside gehören können.
|
||||||
|
*/
|
||||||
public Set<String> getRightsides(String leftside) {
|
public Set<String> getRightsides(String leftside) {
|
||||||
return this.rules.stream()
|
return this.rules.stream()
|
||||||
.filter(rule -> rule.getLeftside().equals(leftside))
|
.filter(rule -> rule.getLeftside().equals(leftside))
|
||||||
@ -249,11 +324,10 @@ public class Grammar {
|
|||||||
&& root.getValue().isEmpty();
|
&& root.getValue().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canPromoteChild(String sym) {
|
private boolean canPromoteChild(String rootName) {
|
||||||
return this.actions.get(PROMOTE).contains(sym);
|
return this.actionMap.get(PROMOTE).contains(rootName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checkt auch auf Anzahl der Kinder und vorhandene Value.
|
* Checkt auch auf Anzahl der Kinder und vorhandene Value.
|
||||||
*/
|
*/
|
||||||
@ -263,11 +337,10 @@ public class Grammar {
|
|||||||
&& root.isEmpty();
|
&& root.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canDeleteIfEmpty(String sym) {
|
public boolean canDeleteIfEmpty(String rootName) {
|
||||||
return this.actions.get(DELIFEMPTY).contains(sym);
|
return this.actionMap.get(DELIFEMPTY).contains(rootName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checkt auch auf Anzahl der Kinder.
|
* Checkt auch auf Anzahl der Kinder.
|
||||||
* Epsilon-Knoten werden immer gelöscht.
|
* Epsilon-Knoten werden immer gelöscht.
|
||||||
@ -277,40 +350,37 @@ public class Grammar {
|
|||||||
&& child.isEmpty();
|
&& child.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canDeleteChild(String parent, String child) {
|
public boolean canDeleteChild(String parentName, String childName) {
|
||||||
return (this.actions.get(DELCHILD).contains(parent)
|
return (this.actionMap.get(DELCHILD).contains(parentName)
|
||||||
&& this.delChildMappings.get(parent).contains(child))
|
&& this.delChildMappings.get(parentName).contains(childName))
|
||||||
|| (child.equals(this.epsilonSymbol));
|
|| (Grammar.EPSILON_SYMBOL.equals(childName));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean canBeRenamed(SyntaxTreeNode root) {
|
public boolean canBeRenamed(SyntaxTreeNode root) {
|
||||||
return this.canBeRenamed(root.getName());
|
return this.canBeRenamed(root.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canBeRenamed(String sym) {
|
public boolean canBeRenamed(String rootName) {
|
||||||
return this.actions.get(RENAMETO).contains(sym);
|
return this.actionMap.get(RENAMETO).contains(rootName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getNewName(SyntaxTreeNode root) {
|
public String getNewName(SyntaxTreeNode root) {
|
||||||
return this.getNewName(root.getName());
|
return this.getNewName(root.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getNewName(String sym) {
|
public String getNewName(String rootName) {
|
||||||
return this.renameMappings.get(sym);
|
return this.renameMappings.get(rootName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean hasValToVal(SyntaxTreeNode parent, SyntaxTreeNode child) {
|
public boolean hasValToVal(SyntaxTreeNode parent, SyntaxTreeNode child) {
|
||||||
return this.hasValToVal(parent.getName(), child.getName());
|
return this.hasValToVal(parent.getName(), child.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasValToVal(String parent, String child) {
|
public boolean hasValToVal(String parentName, String childName) {
|
||||||
return this.actions.get(VALTOVAL).contains(parent)
|
return this.actionMap.get(VALTOVAL).contains(parentName)
|
||||||
&& this.valToValMappings.get(parent).contains(child);
|
&& this.valToValMappings.get(parentName).contains(childName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checkt auch auf bereits existierende Values.
|
* Checkt auch auf bereits existierende Values.
|
||||||
*/
|
*/
|
||||||
@ -319,8 +389,8 @@ public class Grammar {
|
|||||||
&& parent.getValue().isEmpty();
|
&& parent.getValue().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean canMoveNameToVal(String parent, String child) {
|
public boolean canMoveNameToVal(String parentName, String childName) {
|
||||||
return this.actions.get(NAMETOVAL).contains(parent)
|
return this.actionMap.get(NAMETOVAL).contains(parentName)
|
||||||
&& this.nameToValMappings.get(parent).contains(child);
|
&& this.nameToValMappings.get(parentName).contains(childName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,9 +46,9 @@ public class GrammarAnalyzer {
|
|||||||
|
|
||||||
// Die Methode funktioniert erst, nachdem first initialisiert ist.
|
// Die Methode funktioniert erst, nachdem first initialisiert ist.
|
||||||
// Deshalb hier doppelt.
|
// Deshalb hier doppelt.
|
||||||
final Predicate<String> nullable = sym -> sym.equals(this.grammar.getEpsilonSymbol())
|
final Predicate<String> nullable = sym -> sym.equals(Grammar.EPSILON_SYMBOL)
|
||||||
|| sym.isBlank()
|
|| sym.isBlank()
|
||||||
|| firstOut.get(sym).contains(this.grammar.getEpsilonSymbol());
|
|| firstOut.get(sym).contains(Grammar.EPSILON_SYMBOL);
|
||||||
final Predicate<String[]> allNullable = split -> split.length == 0
|
final Predicate<String[]> allNullable = split -> split.length == 0
|
||||||
|| Arrays.stream(split).allMatch(nullable);
|
|| Arrays.stream(split).allMatch(nullable);
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ public class GrammarAnalyzer {
|
|||||||
for (String rightside : this.grammar.getRightsides(leftside)) {
|
for (String rightside : this.grammar.getRightsides(leftside)) {
|
||||||
// ...and X -> Y1 Y2 ... Yk is a production...
|
// ...and X -> Y1 Y2 ... Yk is a production...
|
||||||
|
|
||||||
if (!rightside.equals(this.grammar.getEpsilonSymbol())) {
|
if (!rightside.equals(Grammar.EPSILON_SYMBOL)) {
|
||||||
// ...for some k >= 1...
|
// ...for some k >= 1...
|
||||||
|
|
||||||
final String[] split = rightside.split(" ");
|
final String[] split = rightside.split(" ");
|
||||||
@ -94,7 +94,7 @@ public class GrammarAnalyzer {
|
|||||||
|
|
||||||
// Because a != epsilon
|
// Because a != epsilon
|
||||||
final Set<String> firstYiNoEps = firstOut.get(split[i]).stream()
|
final Set<String> firstYiNoEps = firstOut.get(split[i]).stream()
|
||||||
.filter(sym -> !sym.equals(this.grammar.getEpsilonSymbol()))
|
.filter(sym -> !sym.equals(Grammar.EPSILON_SYMBOL))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
final boolean changeNow = firstOut.get(leftside).addAll(firstYiNoEps);
|
final boolean changeNow = firstOut.get(leftside).addAll(firstYiNoEps);
|
||||||
@ -106,21 +106,21 @@ public class GrammarAnalyzer {
|
|||||||
if (i == split.length - 1 && allNullable.test(split)) {
|
if (i == split.length - 1 && allNullable.test(split)) {
|
||||||
// 2. (b) If epsilon is in first(Y1) ... first(Yk), then add epsilon to first(X).
|
// 2. (b) If epsilon is in first(Y1) ... first(Yk), then add epsilon to first(X).
|
||||||
|
|
||||||
final boolean changeNow = firstOut.get(leftside).add(this.grammar.getEpsilonSymbol());
|
final boolean changeNow = firstOut.get(leftside).add(Grammar.EPSILON_SYMBOL);
|
||||||
change = change || changeNow;
|
change = change || changeNow;
|
||||||
|
|
||||||
logIfTrue(changeNow, "First: Added " + this.grammar.getEpsilonSymbol() + " to " + leftside + " (All are nullable)");
|
logIfTrue(changeNow, "First: Added " + Grammar.EPSILON_SYMBOL + " to " + leftside + " (All are nullable)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rightside.equals(this.grammar.getEpsilonSymbol())) {
|
if (rightside.equals(Grammar.EPSILON_SYMBOL)) {
|
||||||
// 3. If X -> epsilon is a production, then add epsilon to first(X).
|
// 3. If X -> epsilon is a production, then add epsilon to first(X).
|
||||||
|
|
||||||
final boolean changeNow = firstOut.get(leftside).add(this.grammar.getEpsilonSymbol());
|
final boolean changeNow = firstOut.get(leftside).add(Grammar.EPSILON_SYMBOL);
|
||||||
change = change || changeNow;
|
change = change || changeNow;
|
||||||
|
|
||||||
logIfTrue(changeNow, "First: Added " + this.grammar.getEpsilonSymbol() + " to " + leftside + " (X -> EPS exists)");
|
logIfTrue(changeNow, "First: Added " + Grammar.EPSILON_SYMBOL + " to " + leftside + " (X -> EPS exists)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -143,7 +143,7 @@ public class GrammarAnalyzer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 1. Place $ in follow(S), where S is the start symbol, and $ is the input right endmarker
|
// 1. Place $ in follow(S), where S is the start symbol, and $ is the input right endmarker
|
||||||
followOut.get(this.grammar.getStartSymbol()).add("$");
|
followOut.get(Grammar.START_SYMBOL).add("$");
|
||||||
|
|
||||||
boolean change;
|
boolean change;
|
||||||
|
|
||||||
@ -176,7 +176,7 @@ public class GrammarAnalyzer {
|
|||||||
if (this.allNullable(sub)) {
|
if (this.allNullable(sub)) {
|
||||||
|
|
||||||
final Set<String> firstXkNoEps = this.first(split[k]).stream()
|
final Set<String> firstXkNoEps = this.first(split[k]).stream()
|
||||||
.filter(sym -> !sym.equals(this.grammar.getEpsilonSymbol()))
|
.filter(sym -> !sym.equals(Grammar.EPSILON_SYMBOL))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
final boolean changeNow = followOut.get(split[i - 1]).addAll(firstXkNoEps);
|
final boolean changeNow = followOut.get(split[i - 1]).addAll(firstXkNoEps);
|
||||||
@ -241,7 +241,7 @@ public class GrammarAnalyzer {
|
|||||||
|
|
||||||
final Set<String> followLeftside = this.follow(leftside);
|
final Set<String> followLeftside = this.follow(leftside);
|
||||||
|
|
||||||
if (firstRightside.contains(this.grammar.getEpsilonSymbol())) {
|
if (firstRightside.contains(Grammar.EPSILON_SYMBOL)) {
|
||||||
// 2. If epsilon in first(a), then...
|
// 2. If epsilon in first(a), then...
|
||||||
|
|
||||||
for (String sym : followLeftside) {
|
for (String sym : followLeftside) {
|
||||||
@ -276,8 +276,8 @@ public class GrammarAnalyzer {
|
|||||||
|
|
||||||
public boolean nullable(String sym) {
|
public boolean nullable(String sym) {
|
||||||
return sym.isBlank()
|
return sym.isBlank()
|
||||||
|| sym.equals(this.grammar.getEpsilonSymbol())
|
|| sym.equals(Grammar.EPSILON_SYMBOL)
|
||||||
|| this.first.get(sym).contains(this.grammar.getEpsilonSymbol());
|
|| this.first.get(sym).contains(Grammar.EPSILON_SYMBOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean allNullable(String[] split) {
|
public boolean allNullable(String[] split) {
|
||||||
@ -304,7 +304,7 @@ public class GrammarAnalyzer {
|
|||||||
// X1 ... Xi-1 are nullable, so first(X1 ... Xn) contains first(Xi)
|
// X1 ... Xi-1 are nullable, so first(X1 ... Xn) contains first(Xi)
|
||||||
|
|
||||||
final Set<String> firstXiNoEps;
|
final Set<String> firstXiNoEps;
|
||||||
if (split.length == 1 && split[0].equals(this.grammar.getEpsilonSymbol())) {
|
if (split.length == 1 && split[0].equals(Grammar.EPSILON_SYMBOL)) {
|
||||||
// Stream collect has to be evaluated, doesn't work on empty stream
|
// Stream collect has to be evaluated, doesn't work on empty stream
|
||||||
|
|
||||||
firstXiNoEps = Collections.emptySet();
|
firstXiNoEps = Collections.emptySet();
|
||||||
@ -312,7 +312,7 @@ public class GrammarAnalyzer {
|
|||||||
// Only non-epsilon symbols
|
// Only non-epsilon symbols
|
||||||
|
|
||||||
firstXiNoEps = this.first(split[i]).stream()
|
firstXiNoEps = this.first(split[i]).stream()
|
||||||
.filter(sym -> !sym.equals(this.grammar.getEpsilonSymbol()))
|
.filter(sym -> !sym.equals(Grammar.EPSILON_SYMBOL))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +321,7 @@ public class GrammarAnalyzer {
|
|||||||
if (i == split.length - 1 && this.allNullable(split)) {
|
if (i == split.length - 1 && this.allNullable(split)) {
|
||||||
// Finally, add epsilon to first(X1 X2 ... Xn) if, for all i, epsilon is in first(Xi).
|
// Finally, add epsilon to first(X1 X2 ... Xn) if, for all i, epsilon is in first(Xi).
|
||||||
|
|
||||||
firstOut.add(this.grammar.getEpsilonSymbol());
|
firstOut.add(Grammar.EPSILON_SYMBOL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,15 +33,12 @@ class GrammarAnalyzerTest {
|
|||||||
final String[] tarray = {"a", "b", "e", "i", "t"};
|
final String[] tarray = {"a", "b", "e", "i", "t"};
|
||||||
terminals = new HashSet<>(Arrays.asList(tarray));
|
terminals = new HashSet<>(Arrays.asList(tarray));
|
||||||
|
|
||||||
final String startSymbol = "S";
|
|
||||||
final String epsilonSymbol = "epsilon";
|
|
||||||
|
|
||||||
final Set<GrammarRule> rules = new HashSet<>();
|
final Set<GrammarRule> rules = new HashSet<>();
|
||||||
rules.add(new GrammarRule("S", "a"));
|
rules.add(new GrammarRule("S", "a"));
|
||||||
rules.add(new GrammarRule("S", "i", "E", "t", "S"));
|
rules.add(new GrammarRule("S", "i", "E", "t", "S"));
|
||||||
rules.add(new GrammarRule("E", "b"));
|
rules.add(new GrammarRule("E", "b"));
|
||||||
|
|
||||||
grammar0 = new Grammar(terminals, nonterminals, startSymbol, epsilonSymbol,
|
grammar0 = new Grammar(terminals, nonterminals,
|
||||||
Collections.emptyMap(), Collections.emptyMap(),
|
Collections.emptyMap(), Collections.emptyMap(),
|
||||||
Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), rules);
|
Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), rules);
|
||||||
}
|
}
|
||||||
@ -53,27 +50,24 @@ class GrammarAnalyzerTest {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
final Set<String> nonterminals;
|
final Set<String> nonterminals;
|
||||||
final String[] narray = {"E", "T", "E2", "T2", "F"};
|
final String[] narray = {"S", "T", "E2", "T2", "F"};
|
||||||
nonterminals = new HashSet<>(Arrays.asList(narray));
|
nonterminals = new HashSet<>(Arrays.asList(narray));
|
||||||
|
|
||||||
final Set<String> terminals;
|
final Set<String> terminals;
|
||||||
final String[] tarray = {"id", "+", "*", "(", ")"};
|
final String[] tarray = {"id", "+", "*", "(", ")"};
|
||||||
terminals = new HashSet<>(Arrays.asList(tarray));
|
terminals = new HashSet<>(Arrays.asList(tarray));
|
||||||
|
|
||||||
final String startSymbol = "E";
|
|
||||||
final String epsilonSymbol = "epsilon";
|
|
||||||
|
|
||||||
final Set<GrammarRule> rules = new HashSet<>();
|
final Set<GrammarRule> rules = new HashSet<>();
|
||||||
rules.add(new GrammarRule("E", "T", "E2"));
|
rules.add(new GrammarRule("S", "T", "E2"));
|
||||||
rules.add(new GrammarRule("E2", "+", "T", "E2"));
|
rules.add(new GrammarRule("E2", "+", "T", "E2"));
|
||||||
rules.add(new GrammarRule("E2", epsilonSymbol));
|
rules.add(new GrammarRule("E2", Grammar.EPSILON_SYMBOL));
|
||||||
rules.add(new GrammarRule("T", "F", "T2"));
|
rules.add(new GrammarRule("T", "F", "T2"));
|
||||||
rules.add(new GrammarRule("T2", "*", "F", "T2"));
|
rules.add(new GrammarRule("T2", "*", "F", "T2"));
|
||||||
rules.add(new GrammarRule("T2", epsilonSymbol));
|
rules.add(new GrammarRule("T2", Grammar.EPSILON_SYMBOL));
|
||||||
rules.add(new GrammarRule("F", "(", "E", ")"));
|
rules.add(new GrammarRule("F", "(", "S", ")"));
|
||||||
rules.add(new GrammarRule("F", "id"));
|
rules.add(new GrammarRule("F", "id"));
|
||||||
|
|
||||||
grammar1 = new Grammar(terminals, nonterminals, startSymbol, epsilonSymbol,
|
grammar1 = new Grammar(terminals, nonterminals,
|
||||||
Collections.emptyMap(), Collections.emptyMap(),
|
Collections.emptyMap(), Collections.emptyMap(),
|
||||||
Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), rules);
|
Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), rules);
|
||||||
}
|
}
|
||||||
@ -90,25 +84,22 @@ class GrammarAnalyzerTest {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
final Set<String> nonterminals;
|
final Set<String> nonterminals;
|
||||||
final String[] narray = {"X", "Y", "Z"};
|
final String[] narray = {"X", "Y", "S"};
|
||||||
nonterminals = new HashSet<>(Arrays.asList(narray));
|
nonterminals = new HashSet<>(Arrays.asList(narray));
|
||||||
|
|
||||||
final Set<String> terminals;
|
final Set<String> terminals;
|
||||||
final String[] tarray = {"a", "c", "d"};
|
final String[] tarray = {"a", "c", "d"};
|
||||||
terminals = new HashSet<>(Arrays.asList(tarray));
|
terminals = new HashSet<>(Arrays.asList(tarray));
|
||||||
|
|
||||||
final String startSymbol = "Z";
|
|
||||||
final String epsilonSymbol = "epsilon";
|
|
||||||
|
|
||||||
final Set<GrammarRule> rules = new HashSet<>();
|
final Set<GrammarRule> rules = new HashSet<>();
|
||||||
rules.add(new GrammarRule("Z", "d"));
|
rules.add(new GrammarRule("S", "d"));
|
||||||
rules.add(new GrammarRule("Z", "X", "Y", "Z"));
|
rules.add(new GrammarRule("S", "X", "Y", "S"));
|
||||||
rules.add(new GrammarRule("Y", epsilonSymbol));
|
rules.add(new GrammarRule("Y", Grammar.EPSILON_SYMBOL));
|
||||||
rules.add(new GrammarRule("Y", "c"));
|
rules.add(new GrammarRule("Y", "c"));
|
||||||
rules.add(new GrammarRule("X", "Y"));
|
rules.add(new GrammarRule("X", "Y"));
|
||||||
rules.add(new GrammarRule("X", "a"));
|
rules.add(new GrammarRule("X", "a"));
|
||||||
|
|
||||||
grammar2 = new Grammar(terminals, nonterminals, startSymbol, epsilonSymbol,
|
grammar2 = new Grammar(terminals, nonterminals,
|
||||||
Collections.emptyMap(), Collections.emptyMap(),
|
Collections.emptyMap(), Collections.emptyMap(),
|
||||||
Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), rules);
|
Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), rules);
|
||||||
}
|
}
|
||||||
@ -125,10 +116,10 @@ class GrammarAnalyzerTest {
|
|||||||
void testFirstGrammar1() {
|
void testFirstGrammar1() {
|
||||||
final GrammarAnalyzer analyzer = new GrammarAnalyzer(grammar1);
|
final GrammarAnalyzer analyzer = new GrammarAnalyzer(grammar1);
|
||||||
|
|
||||||
assertThat(analyzer.getFirst().get("E")).containsOnly("id", "(");
|
assertThat(analyzer.getFirst().get("S")).containsOnly("id", "(");
|
||||||
assertThat(analyzer.getFirst().get("E2")).containsOnly("+", grammar1.getEpsilonSymbol());
|
assertThat(analyzer.getFirst().get("E2")).containsOnly("+", Grammar.EPSILON_SYMBOL);
|
||||||
assertThat(analyzer.getFirst().get("T")).containsOnly("id", "(");
|
assertThat(analyzer.getFirst().get("T")).containsOnly("id", "(");
|
||||||
assertThat(analyzer.getFirst().get("T2")).containsOnly("*", grammar1.getEpsilonSymbol());
|
assertThat(analyzer.getFirst().get("T2")).containsOnly("*", Grammar.EPSILON_SYMBOL);
|
||||||
assertThat(analyzer.getFirst().get("F")).containsOnly("id", "(");
|
assertThat(analyzer.getFirst().get("F")).containsOnly("id", "(");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,9 +127,9 @@ class GrammarAnalyzerTest {
|
|||||||
void testFirstGrammar2() {
|
void testFirstGrammar2() {
|
||||||
final GrammarAnalyzer analyzer = new GrammarAnalyzer(grammar2);
|
final GrammarAnalyzer analyzer = new GrammarAnalyzer(grammar2);
|
||||||
|
|
||||||
assertThat(analyzer.getFirst().get("X")).containsOnly("c", "a", grammar2.getEpsilonSymbol());
|
assertThat(analyzer.getFirst().get("X")).containsOnly("c", "a", Grammar.EPSILON_SYMBOL);
|
||||||
assertThat(analyzer.getFirst().get("Y")).containsOnly("c", grammar2.getEpsilonSymbol());
|
assertThat(analyzer.getFirst().get("Y")).containsOnly("c", Grammar.EPSILON_SYMBOL);
|
||||||
assertThat(analyzer.getFirst().get("Z")).containsOnly("c", "a", "d");
|
assertThat(analyzer.getFirst().get("S")).containsOnly("c", "a", "d");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -153,7 +144,7 @@ class GrammarAnalyzerTest {
|
|||||||
void testFollowGrammar1() {
|
void testFollowGrammar1() {
|
||||||
final GrammarAnalyzer analyzer = new GrammarAnalyzer(grammar1);
|
final GrammarAnalyzer analyzer = new GrammarAnalyzer(grammar1);
|
||||||
|
|
||||||
assertThat(analyzer.getFollow().get("E")).containsOnly(")", "$");
|
assertThat(analyzer.getFollow().get("S")).containsOnly(")", "$");
|
||||||
assertThat(analyzer.getFollow().get("E2")).containsOnly(")", "$");
|
assertThat(analyzer.getFollow().get("E2")).containsOnly(")", "$");
|
||||||
assertThat(analyzer.getFollow().get("T")).containsOnly("+", ")", "$");
|
assertThat(analyzer.getFollow().get("T")).containsOnly("+", ")", "$");
|
||||||
assertThat(analyzer.getFollow().get("T2")).containsOnly("+", ")", "$");
|
assertThat(analyzer.getFollow().get("T2")).containsOnly("+", ")", "$");
|
||||||
@ -166,7 +157,7 @@ class GrammarAnalyzerTest {
|
|||||||
|
|
||||||
assertThat(analyzer.getFollow().get("X")).containsOnly("a", "c", "d");
|
assertThat(analyzer.getFollow().get("X")).containsOnly("a", "c", "d");
|
||||||
assertThat(analyzer.getFollow().get("Y")).containsOnly("a", "c", "d");
|
assertThat(analyzer.getFollow().get("Y")).containsOnly("a", "c", "d");
|
||||||
assertThat(analyzer.getFollow().get("Z")).containsOnly("$");
|
assertThat(analyzer.getFollow().get("S")).containsOnly("$");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -174,18 +165,18 @@ class GrammarAnalyzerTest {
|
|||||||
final GrammarAnalyzer analyzer = new GrammarAnalyzer(grammar1);
|
final GrammarAnalyzer analyzer = new GrammarAnalyzer(grammar1);
|
||||||
final ParsingTable table = analyzer.getTable();
|
final ParsingTable table = analyzer.getTable();
|
||||||
|
|
||||||
assertThat(table.get("E", "id")).isEqualTo("T E2");
|
assertThat(table.get("S", "id")).isEqualTo("T E2");
|
||||||
assertThat(table.get("E", "(")).isEqualTo("T E2");
|
assertThat(table.get("S", "(")).isEqualTo("T E2");
|
||||||
assertThat(table.get("E2", "+")).isEqualTo("+ T E2");
|
assertThat(table.get("E2", "+")).isEqualTo("+ T E2");
|
||||||
assertThat(table.get("E2", ")")).isEqualTo(grammar1.getEpsilonSymbol());
|
assertThat(table.get("E2", ")")).isEqualTo(Grammar.EPSILON_SYMBOL);
|
||||||
assertThat(table.get("E2", "$")).isEqualTo(grammar1.getEpsilonSymbol());
|
assertThat(table.get("E2", "$")).isEqualTo(Grammar.EPSILON_SYMBOL);
|
||||||
assertThat(table.get("T", "id")).isEqualTo("F T2");
|
assertThat(table.get("T", "id")).isEqualTo("F T2");
|
||||||
assertThat(table.get("T", "(")).isEqualTo("F T2");
|
assertThat(table.get("T", "(")).isEqualTo("F T2");
|
||||||
assertThat(table.get("T2", "+")).isEqualTo(grammar1.getEpsilonSymbol());
|
assertThat(table.get("T2", "+")).isEqualTo(Grammar.EPSILON_SYMBOL);
|
||||||
assertThat(table.get("T2", "*")).isEqualTo("* F T2");
|
assertThat(table.get("T2", "*")).isEqualTo("* F T2");
|
||||||
assertThat(table.get("T2", ")")).isEqualTo(grammar1.getEpsilonSymbol());
|
assertThat(table.get("T2", ")")).isEqualTo(Grammar.EPSILON_SYMBOL);
|
||||||
assertThat(table.get("T2", "$")).isEqualTo(grammar1.getEpsilonSymbol());
|
assertThat(table.get("T2", "$")).isEqualTo(Grammar.EPSILON_SYMBOL);
|
||||||
assertThat(table.get("F", "id")).isEqualTo("id");
|
assertThat(table.get("F", "id")).isEqualTo("id");
|
||||||
assertThat(table.get("F", "(")).isEqualTo("( E )");
|
assertThat(table.get("F", "(")).isEqualTo("( S )");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,10 +26,7 @@ class GrammarTest {
|
|||||||
final Path path = getPath("SimpleGrammar0.grammar");
|
final Path path = getPath("SimpleGrammar0.grammar");
|
||||||
|
|
||||||
final Grammar grammar = Grammar.fromFile(path);
|
final Grammar grammar = Grammar.fromFile(path);
|
||||||
assert grammar != null;
|
|
||||||
|
|
||||||
assertThat(grammar.getEpsilonSymbol()).isEqualTo("epsilon");
|
|
||||||
assertThat(grammar.getStartSymbol()).isEqualTo("S");
|
|
||||||
assertThat(grammar.getTerminals()).containsOnly("a", "i", "t", "b");
|
assertThat(grammar.getTerminals()).containsOnly("a", "i", "t", "b");
|
||||||
assertThat(grammar.getNonterminals()).containsOnly("S", "E");
|
assertThat(grammar.getNonterminals()).containsOnly("S", "E");
|
||||||
assertThat(grammar.getRules()).containsOnly(new GrammarRule("S", "a"),
|
assertThat(grammar.getRules()).containsOnly(new GrammarRule("S", "a"),
|
||||||
@ -42,14 +39,11 @@ class GrammarTest {
|
|||||||
final Path path = getPath("SimpleGrammar1.grammar");
|
final Path path = getPath("SimpleGrammar1.grammar");
|
||||||
|
|
||||||
final Grammar grammar = Grammar.fromFile(path);
|
final Grammar grammar = Grammar.fromFile(path);
|
||||||
assert grammar != null;
|
|
||||||
|
|
||||||
assertThat(grammar.getEpsilonSymbol()).isEqualTo("epsilon");
|
|
||||||
assertThat(grammar.getStartSymbol()).isEqualTo("E");
|
|
||||||
assertThat(grammar.getTerminals()).containsOnly("id", "+", "*", "(", ")");
|
assertThat(grammar.getTerminals()).containsOnly("id", "+", "*", "(", ")");
|
||||||
assertThat(grammar.getNonterminals()).containsOnly("E", "E2", "T", "T2", "F");
|
assertThat(grammar.getNonterminals()).containsOnly("S", "E2", "T", "T2", "F");
|
||||||
assertThat(grammar.getRules()).contains(new GrammarRule("E", "T", "E2"),
|
assertThat(grammar.getRules()).contains(new GrammarRule("S", "T", "E2"),
|
||||||
new GrammarRule("E2", "+", "T", "E2"),
|
new GrammarRule("E2", "+", "T", "E2"),
|
||||||
new GrammarRule("E2", "epsilon"));
|
new GrammarRule("E2", Grammar.EPSILON_SYMBOL));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
START: s
|
// NTERM, TERM are reserved
|
||||||
EPS: eps
|
|
||||||
|
|
||||||
// START, EPS, NTERM, TERM are reserved
|
|
||||||
|
|
||||||
// Some Grammar-Symbols have to be named this way:
|
// Some Grammar-Symbols have to be named this way:
|
||||||
// assignment, declaration (for TypeTable creation)
|
// assignment, declaration (for TypeTable creation)
|
||||||
@ -11,7 +8,7 @@ EPS: eps
|
|||||||
// Nonterminals:
|
// Nonterminals:
|
||||||
NTERM: val type
|
NTERM: val type
|
||||||
NTERM: op unary arith_op logic_op compare_op
|
NTERM: op unary arith_op logic_op compare_op
|
||||||
NTERM: s class_cnt block_cnt
|
NTERM: S class_cnt block_cnt
|
||||||
NTERM: statement stmt print
|
NTERM: statement stmt print
|
||||||
NTERM: declaration assignment
|
NTERM: declaration assignment
|
||||||
NTERM: par_expr expr expr_2 expr_f
|
NTERM: par_expr expr expr_2 expr_f
|
||||||
@ -64,7 +61,7 @@ compare_op[promote] -> LESS | LESS_EQUAL | GREATER | GREATER_EQUAL | EQUAL | NOT
|
|||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// START -> class IDENTIFIER { class_cnt }
|
// START -> class IDENTIFIER { class_cnt }
|
||||||
s[promote] -> CLASS IDENTIFIER L_BRACE class_cnt R_BRACE | eps
|
S[promote] -> CLASS IDENTIFIER L_BRACE class_cnt R_BRACE | eps
|
||||||
|
|
||||||
// class_cnt -> public static void main(String[] args) { block_cnt }
|
// class_cnt -> public static void main(String[] args) { block_cnt }
|
||||||
class_cnt[promote delifempty] -> PUBLIC STATIC VOID_TYPE IDENTIFIER_MAIN L_PAREN STRING_TYPE L_BRACKET R_BRACKET IDENTIFIER R_PAREN L_BRACE block_cnt R_BRACE | eps
|
class_cnt[promote delifempty] -> PUBLIC STATIC VOID_TYPE IDENTIFIER_MAIN L_PAREN STRING_TYPE L_BRACKET R_BRACKET IDENTIFIER R_PAREN L_BRACE block_cnt R_BRACE | eps
|
||||||
|
@ -1,5 +1,3 @@
|
|||||||
START: S
|
|
||||||
EPS: epsilon
|
|
||||||
TERM: a i t b
|
TERM: a i t b
|
||||||
NTERM: S E
|
NTERM: S E
|
||||||
|
|
||||||
|
@ -1,13 +1,11 @@
|
|||||||
START: E
|
|
||||||
EPS: epsilon
|
|
||||||
TERM: id + * ( )
|
TERM: id + * ( )
|
||||||
NTERM: E E2 T T2 F
|
NTERM: S E2 T T2 F
|
||||||
|
|
||||||
// Leerzeilen sind egal
|
// Leerzeilen sind egal
|
||||||
// Man kann Line-Comments schreiben und Produktionen verodern
|
// Man kann Line-Comments schreiben und Produktionen verodern
|
||||||
|
|
||||||
E -> T E2
|
S -> T E2
|
||||||
E2 -> + T E2 | EPS
|
E2 -> + T E2 | eps
|
||||||
T -> F T2
|
T -> F T2
|
||||||
T2 -> * F T2 | EPS
|
T2 -> * F T2 | eps
|
||||||
F -> ( E ) | id
|
F -> ( S ) | id
|
||||||
|
@ -1,7 +1,4 @@
|
|||||||
START: s
|
// NTERM, TERM are reserved
|
||||||
EPS: eps
|
|
||||||
|
|
||||||
// START, EPS, NTERM, TERM are reserved
|
|
||||||
|
|
||||||
// Some Grammar-Symbols have to be named this way:
|
// Some Grammar-Symbols have to be named this way:
|
||||||
// assignment, declaration (for TypeTable creation)
|
// assignment, declaration (for TypeTable creation)
|
||||||
@ -11,7 +8,7 @@ EPS: eps
|
|||||||
// Nonterminals:
|
// Nonterminals:
|
||||||
NTERM: val type
|
NTERM: val type
|
||||||
NTERM: op unary arith_op logic_op compare_op
|
NTERM: op unary arith_op logic_op compare_op
|
||||||
NTERM: s class_cnt block_cnt
|
NTERM: S class_cnt block_cnt
|
||||||
NTERM: statement stmt print
|
NTERM: statement stmt print
|
||||||
NTERM: declaration assignment
|
NTERM: declaration assignment
|
||||||
NTERM: par_expr expr expr_2 expr_f
|
NTERM: par_expr expr expr_2 expr_f
|
||||||
@ -64,7 +61,7 @@ compare_op[promote] -> LESS | LESS_EQUAL | GREATER | GREATER_EQUAL | EQUAL | NOT
|
|||||||
// -------------------------------------------------------------------------------------------------
|
// -------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
// START -> class IDENTIFIER { class_cnt }
|
// START -> class IDENTIFIER { class_cnt }
|
||||||
s[promote] -> CLASS IDENTIFIER L_BRACE class_cnt R_BRACE | eps
|
S[promote] -> CLASS IDENTIFIER L_BRACE class_cnt R_BRACE | eps
|
||||||
|
|
||||||
// class_cnt -> public static void main(String[] args) { block_cnt }
|
// class_cnt -> public static void main(String[] args) { block_cnt }
|
||||||
class_cnt[promote delifempty] -> PUBLIC STATIC VOID_TYPE IDENTIFIER_MAIN L_PAREN STRING_TYPE L_BRACKET R_BRACKET IDENTIFIER R_PAREN L_BRACE block_cnt R_BRACE | eps
|
class_cnt[promote delifempty] -> PUBLIC STATIC VOID_TYPE IDENTIFIER_MAIN L_PAREN STRING_TYPE L_BRACKET R_BRACKET IDENTIFIER R_PAREN L_BRACE block_cnt R_BRACE | eps
|
||||||
|
Reference in New Issue
Block a user