use logger for logging + fix bug in stringFirst
This commit is contained in:
@ -11,6 +11,8 @@ import java.util.ArrayDeque;
|
|||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import static util.tools.Logger.log;
|
||||||
|
|
||||||
public class LL1Parser {
|
public class LL1Parser {
|
||||||
|
|
||||||
private final ILL1ParsingTable parsetable;
|
private final ILL1ParsingTable parsetable;
|
||||||
@ -35,7 +37,9 @@ public class LL1Parser {
|
|||||||
stack.push(root);
|
stack.push(root);
|
||||||
|
|
||||||
int inputPosition = 0;
|
int inputPosition = 0;
|
||||||
System.out.println("\nParsing " + token + ":");
|
|
||||||
|
log("\nParsing:");
|
||||||
|
log("Input: " + token + "\n");
|
||||||
|
|
||||||
// Parsing
|
// Parsing
|
||||||
while (!stack.isEmpty()) {
|
while (!stack.isEmpty()) {
|
||||||
@ -76,8 +80,7 @@ public class LL1Parser {
|
|||||||
// Wenn das Nichtterminal auf dem Stack durch (s)eine Produktion ersetzt werden kann
|
// Wenn das Nichtterminal auf dem Stack durch (s)eine Produktion ersetzt werden kann
|
||||||
// Hier wird auch der AST aufgebaut
|
// Hier wird auch der AST aufgebaut
|
||||||
|
|
||||||
System.out.println(top + " -> " + prod);
|
log("Used: " + top + " -> " + prod);
|
||||||
|
|
||||||
Node pop = stack.pop();
|
Node pop = stack.pop();
|
||||||
|
|
||||||
final String[] split = prod.split(" ");
|
final String[] split = prod.split(" ");
|
||||||
@ -89,7 +92,9 @@ public class LL1Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("\n" + tree);
|
log("\nParsed AST:\n" + tree);
|
||||||
|
log("-".repeat(100) + "\n");
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,6 +13,10 @@ import java.util.Set;
|
|||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import static util.tools.Logger.log;
|
||||||
|
import static util.tools.Logger.logIfTrue;
|
||||||
|
import static util.tools.Logger.logNullable;
|
||||||
|
|
||||||
public class LL1GrammarAnalyzer {
|
public class LL1GrammarAnalyzer {
|
||||||
|
|
||||||
private final Grammar grammar;
|
private final Grammar grammar;
|
||||||
@ -25,15 +29,14 @@ public class LL1GrammarAnalyzer {
|
|||||||
public LL1GrammarAnalyzer(Grammar grammar) {
|
public LL1GrammarAnalyzer(Grammar grammar) {
|
||||||
this.grammar = grammar;
|
this.grammar = grammar;
|
||||||
|
|
||||||
|
log("-".repeat(100));
|
||||||
|
log("Analyzing Grammar:\n");
|
||||||
|
|
||||||
// Es muss zwingend in der Reihenfolge [Nullable < First < Follow < Table] initialisiert werden
|
// Es muss zwingend in der Reihenfolge [Nullable < First < Follow < Table] initialisiert werden
|
||||||
this.first = this.initFirst();
|
this.first = this.initFirst();
|
||||||
this.follow = this.initFollow();
|
this.follow = this.initFollow();
|
||||||
|
|
||||||
this.table = this.initParseTable();
|
this.table = this.initParseTable();
|
||||||
|
|
||||||
// System.out.println("First:\n" + this.first);
|
|
||||||
// System.out.println("Follow:\n" + this.follow);
|
|
||||||
System.out.println("LL-Table:\n" + this.table);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Set<String>> initFirst() {
|
private Map<String, Set<String>> initFirst() {
|
||||||
@ -47,6 +50,8 @@ public class LL1GrammarAnalyzer {
|
|||||||
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);
|
||||||
|
|
||||||
|
log("First Initialisieren:");
|
||||||
|
|
||||||
// Initialisieren
|
// Initialisieren
|
||||||
for (String nterm : this.grammar.getNonterminals()) {
|
for (String nterm : this.grammar.getNonterminals()) {
|
||||||
firstOut.put(nterm, new HashSet<>());
|
firstOut.put(nterm, new HashSet<>());
|
||||||
@ -90,13 +95,19 @@ public class LL1GrammarAnalyzer {
|
|||||||
.filter(sym -> !sym.equals(this.grammar.getEpsilonSymbol()))
|
.filter(sym -> !sym.equals(this.grammar.getEpsilonSymbol()))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
change = change || firstOut.get(leftside).addAll(firstYiNoEps);
|
boolean changeNow = firstOut.get(leftside).addAll(firstYiNoEps);
|
||||||
|
change = change || changeNow;
|
||||||
|
|
||||||
|
logIfTrue(changeNow, "First: Added " + firstYiNoEps + " to " + leftside + " (All before are nullable)");
|
||||||
}
|
}
|
||||||
|
|
||||||
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).
|
||||||
|
|
||||||
change = change || firstOut.get(leftside).add(this.grammar.getEpsilonSymbol());
|
boolean changeNow = firstOut.get(leftside).add(this.grammar.getEpsilonSymbol());
|
||||||
|
change = change || changeNow;
|
||||||
|
|
||||||
|
logIfTrue(changeNow, "First: Added " + this.grammar.getEpsilonSymbol() + " to " + leftside + " (All are nullable)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -104,18 +115,26 @@ public class LL1GrammarAnalyzer {
|
|||||||
if (rightside.equals(this.grammar.getEpsilonSymbol())) {
|
if (rightside.equals(this.grammar.getEpsilonSymbol())) {
|
||||||
// 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).
|
||||||
|
|
||||||
change = change || firstOut.get(leftside).add(this.grammar.getEpsilonSymbol());
|
boolean changeNow = firstOut.get(leftside).add(this.grammar.getEpsilonSymbol());
|
||||||
|
change = change || changeNow;
|
||||||
|
|
||||||
|
logIfTrue(changeNow, "First: Added " + this.grammar.getEpsilonSymbol() + " to " + leftside + " (X -> EPS exists)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} while (change);
|
} while (change);
|
||||||
|
|
||||||
|
log("\n" + firstOut);
|
||||||
|
log("-".repeat(100) + "\n");
|
||||||
|
|
||||||
return firstOut;
|
return firstOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Set<String>> initFollow() {
|
private Map<String, Set<String>> initFollow() {
|
||||||
final Map<String, Set<String>> followOut = new HashMap<>();
|
final Map<String, Set<String>> followOut = new HashMap<>();
|
||||||
|
|
||||||
|
log("Follow Initialisieren:");
|
||||||
|
|
||||||
// Initialisieren
|
// Initialisieren
|
||||||
for (String nterm : this.grammar.getNonterminals()) {
|
for (String nterm : this.grammar.getNonterminals()) {
|
||||||
followOut.put(nterm, new HashSet<>());
|
followOut.put(nterm, new HashSet<>());
|
||||||
@ -158,7 +177,10 @@ public class LL1GrammarAnalyzer {
|
|||||||
.filter(sym -> !sym.equals(this.grammar.getEpsilonSymbol()))
|
.filter(sym -> !sym.equals(this.grammar.getEpsilonSymbol()))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
change = change || followOut.get(split[i - 1]).addAll(firstXkNoEps);
|
boolean changeNow = followOut.get(split[i - 1]).addAll(firstXkNoEps);
|
||||||
|
change = change || changeNow;
|
||||||
|
|
||||||
|
logIfTrue(changeNow, "Follow: Added " + firstXkNoEps + " to " + split[i - 1] + " (Dazwischen nullable)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -168,26 +190,37 @@ public class LL1GrammarAnalyzer {
|
|||||||
|
|
||||||
if (this.allNullable(sub)) {
|
if (this.allNullable(sub)) {
|
||||||
|
|
||||||
change = change || followOut.get(split[i - 1]).addAll(followOut.get(leftside));
|
boolean changeNow = followOut.get(split[i - 1]).addAll(followOut.get(leftside));
|
||||||
|
change = change || changeNow;
|
||||||
|
|
||||||
|
logIfTrue(changeNow, "Follow: Added " + leftside + " to " + split[i - 1] + " (Dahinter nullable)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.grammar.getNonterminals().contains(split[split.length - 1])) {
|
if (this.grammar.getNonterminals().contains(split[split.length - 1])) {
|
||||||
// 3. (a) If there is a production A -> aB, then everything in follow(A) is in follow(B).
|
// 3. (a) If there is a production A -> aB, then everything in follow(A) is in follow(B).
|
||||||
|
|
||||||
change = change || followOut.get(split[split.length - 1]).addAll(followOut.get(leftside));
|
boolean changeNow = followOut.get(split[split.length - 1]).addAll(followOut.get(leftside));
|
||||||
|
change = change || changeNow;
|
||||||
|
|
||||||
|
logIfTrue(changeNow, "Follow: Added " + followOut.get(leftside) + " to " + split[split.length - 1] + " (Ende der Regel)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} while (change);
|
} while (change);
|
||||||
|
|
||||||
|
log("\n" + followOut);
|
||||||
|
log("-".repeat(100) + "\n");
|
||||||
|
|
||||||
return followOut;
|
return followOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
private ILL1ParsingTable initParseTable() {
|
private ILL1ParsingTable initParseTable() {
|
||||||
Map<Map.Entry<String, String>, String> tableOut = new HashMap<>();
|
Map<Map.Entry<String, String>, String> tableOut = new HashMap<>();
|
||||||
|
|
||||||
|
log("Parsetable Aufstellen:");
|
||||||
|
|
||||||
for (String leftside : this.grammar.getLeftSides()) {
|
for (String leftside : this.grammar.getLeftSides()) {
|
||||||
|
|
||||||
for (String rightside : this.grammar.getRightsides(leftside)) {
|
for (String rightside : this.grammar.getRightsides(leftside)) {
|
||||||
@ -198,13 +231,17 @@ public class LL1GrammarAnalyzer {
|
|||||||
for (String sym : firstRightside) {
|
for (String sym : firstRightside) {
|
||||||
// 1. For each terminal t in first(a), add A -> a to table[A, t]
|
// 1. For each terminal t in first(a), add A -> a to table[A, t]
|
||||||
|
|
||||||
tableOut.put(new AbstractMap.SimpleEntry<>(leftside, sym), rightside);
|
String prev = tableOut.put(new AbstractMap.SimpleEntry<>(leftside, sym), rightside);
|
||||||
|
|
||||||
|
log("Add " + rightside + " to cell (" + leftside + ", " + sym + ") (" + sym + " in first of " + rightside + ")");
|
||||||
|
logNullable("Overwritten " + prev + "!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
final Set<String> followLeftside = this.follow(leftside);
|
final Set<String> followLeftside = this.follow(leftside);
|
||||||
|
|
||||||
System.out.println(leftside + " -> " + rightside);
|
// log(leftside + " -> " + rightside);
|
||||||
System.out.println("First: " + firstRightside);
|
// log("First: " + firstRightside);
|
||||||
|
// log("Follow: " + followLeftside + "\n");
|
||||||
|
|
||||||
if (firstRightside.contains(this.grammar.getEpsilonSymbol())) {
|
if (firstRightside.contains(this.grammar.getEpsilonSymbol())) {
|
||||||
// 2. If epsilon in first(a), then...
|
// 2. If epsilon in first(a), then...
|
||||||
@ -212,19 +249,30 @@ public class LL1GrammarAnalyzer {
|
|||||||
for (String sym : followLeftside) {
|
for (String sym : followLeftside) {
|
||||||
// ...for each terminal b in follow(A), add A -> a to table[A, b].
|
// ...for each terminal b in follow(A), add A -> a to table[A, b].
|
||||||
|
|
||||||
tableOut.put(new AbstractMap.SimpleEntry<>(leftside, sym), rightside);
|
String prev = tableOut.put(new AbstractMap.SimpleEntry<>(leftside, sym), rightside);
|
||||||
|
|
||||||
|
log("Add " + rightside + " to cell (" + leftside + ", " + sym + ") (" + sym + " in follow of " + leftside + ")");
|
||||||
|
logNullable("Overwritten " + prev + "!\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (followLeftside.contains("$")) {
|
if (followLeftside.contains("$")) {
|
||||||
// If epsilon is in first(a) and $ is in follow(A), add A -> a to table[A, $].
|
// If epsilon is in first(a) and $ is in follow(A), add A -> a to table[A, $].
|
||||||
|
|
||||||
tableOut.put(new AbstractMap.SimpleEntry<>(leftside, "$"), rightside);
|
String prev = tableOut.put(new AbstractMap.SimpleEntry<>(leftside, "$"), rightside);
|
||||||
|
|
||||||
|
log("Add " + rightside + " to cell (" + leftside + ", $) (epsilon in first of " + rightside + " and $ in follow of " + leftside + ")");
|
||||||
|
logNullable("Overwritten " + prev + "!\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new LL1ParsingTable(this.grammar, tableOut);
|
final LL1ParsingTable table = new LL1ParsingTable(this.grammar, tableOut);
|
||||||
|
|
||||||
|
log("\n" + table);
|
||||||
|
log("-".repeat(100) + "\n");
|
||||||
|
|
||||||
|
return table;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -277,7 +325,7 @@ public class LL1GrammarAnalyzer {
|
|||||||
|
|
||||||
firstOut.addAll(firstXiNoEps);
|
firstOut.addAll(firstXiNoEps);
|
||||||
|
|
||||||
if (i == split.length - 1) {
|
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(this.grammar.getEpsilonSymbol());
|
||||||
|
|||||||
Reference in New Issue
Block a user