implement grammaranalyzer

This commit is contained in:
ChUrl
2020-12-04 01:21:21 +01:00
parent 11a1591b9b
commit 2c4b95c438
6 changed files with 473 additions and 165 deletions

View File

@ -16,6 +16,10 @@ import org.antlr.v4.runtime.dfa.DFA;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class StupsLexer extends Lexer {
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
private static final String[] _LITERAL_NAMES = makeLiteralNames();
public static final int
WHITESPACE = 1, MULTILINE_COMMENT = 2, LINE_COMMENT = 3, CLASS = 4, PUBLIC = 5,
STATIC = 6, VOID_TYPE = 7, BOOLEAN_TYPE = 8, STRING_TYPE = 9, IF = 10, ELSE = 11,
@ -24,12 +28,111 @@ public class StupsLexer extends Lexer {
GREATER = 27, GREATER_EQUAL = 28, L_PAREN = 29, R_PAREN = 30, L_BRACE = 31, R_BRACE = 32,
L_BRACKET = 33, R_BRACKET = 34, SEMICOLON = 35, COMMA = 36, DOT = 37, INTEGER_LIT = 38,
STRING_LIT = 39, BOOLEAN_LIT = 40, IDENTIFIER = 41;
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
public static String[] channelNames = {
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
};
public static final String[] ruleNames = makeRuleNames();
public static String[] modeNames = {
"DEFAULT_MODE"
};
static {
RuntimeMetaData.checkVersion("4.8", RuntimeMetaData.VERSION);
}
static {
tokenNames = new String[_SYMBOLIC_NAMES.length];
for (int i = 0; i < tokenNames.length; i++) {
tokenNames[i] = VOCABULARY.getLiteralName(i);
if (tokenNames[i] == null) {
tokenNames[i] = VOCABULARY.getSymbolicName(i);
}
if (tokenNames[i] == null) {
tokenNames[i] = "<INVALID>";
}
}
}
public StupsLexer(CharStream input) {
super(input);
_interp = new LexerATNSimulator(this, _ATN, _decisionToDFA, _sharedContextCache);
}
private static String[] makeRuleNames() {
return new String[]{
"FIRST_DIGIT", "DIGIT", "LOWERCASE", "UPPERCASE", "LETTER", "LETTER_DIGIT",
"WHITE", "ANY", "ANY_NOBREAK", "ANY_NOWHITE", "WHITESPACE", "MULTILINE_COMMENT",
"LINE_COMMENT", "CLASS", "PUBLIC", "STATIC", "VOID_TYPE", "BOOLEAN_TYPE",
"STRING_TYPE", "IF", "ELSE", "WHILE", "PRINTLN", "ASSIGN", "ADD", "SUB",
"MUL", "DIV", "MOD", "NOT", "AND", "OR", "EQUAL", "NOT_EQUAL", "LESS",
"LESS_EQUAL", "GREATER", "GREATER_EQUAL", "L_PAREN", "R_PAREN", "L_BRACE",
"R_BRACE", "L_BRACKET", "R_BRACKET", "SEMICOLON", "COMMA", "DOT", "INTEGER_LIT",
"STRING_LIT", "BOOLEAN_LIT", "IDENTIFIER"
};
}
/**
* @deprecated Use {@link #VOCABULARY} instead.
*/
@Deprecated
public static final String[] tokenNames;
private static String[] makeLiteralNames() {
return new String[]{
null, null, null, null, "'class'", "'public'", "'static'", "'void'",
"'boolean'", "'String'", "'if'", "'else'", "'while'", "'System.out.println'",
"'='", "'+'", "'-'", "'*'", "'/'", "'%'", "'!'", "'&&'", "'||'", "'=='",
"'!='", "'<'", "'<='", "'>'", "'>='", "'('", "')'", "'{'", "'}'", "'['",
"']'", "';'", "','", "'.'"
};
}
private static String[] makeSymbolicNames() {
return new String[]{
null, "WHITESPACE", "MULTILINE_COMMENT", "LINE_COMMENT", "CLASS", "PUBLIC",
"STATIC", "VOID_TYPE", "BOOLEAN_TYPE", "STRING_TYPE", "IF", "ELSE", "WHILE",
"PRINTLN", "ASSIGN", "ADD", "SUB", "MUL", "DIV", "MOD", "NOT", "AND",
"OR", "EQUAL", "NOT_EQUAL", "LESS", "LESS_EQUAL", "GREATER", "GREATER_EQUAL",
"L_PAREN", "R_PAREN", "L_BRACE", "R_BRACE", "L_BRACKET", "R_BRACKET",
"SEMICOLON", "COMMA", "DOT", "INTEGER_LIT", "STRING_LIT", "BOOLEAN_LIT",
"IDENTIFIER"
};
}
@Override
public String[] getRuleNames() { return ruleNames; }
@Override
public Vocabulary getVocabulary() {
return VOCABULARY;
}
@Override
public String getSerializedATN() { return _serializedATN; }
@Override
public String getGrammarFileName() { return "StupsLexer.g4"; }
@Override
public ATN getATN() { return _ATN; }
@Override
public String[] getChannelNames() { return channelNames; }
@Override
public String[] getModeNames() { return modeNames; }
@Override
@Deprecated
public String[] getTokenNames() {
return tokenNames;
}
public static final String _serializedATN =
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2+\u0143\b\1\4\2\t" +
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13" +
@ -136,36 +239,6 @@ public class StupsLexer extends Lexer {
"\u0140\3\b\2\2";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
private static final String[] _LITERAL_NAMES = makeLiteralNames();
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
public static String[] channelNames = {
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
};
public static String[] modeNames = {
"DEFAULT_MODE"
};
static {
RuntimeMetaData.checkVersion("4.8", RuntimeMetaData.VERSION);
}
static {
tokenNames = new String[_SYMBOLIC_NAMES.length];
for (int i = 0; i < tokenNames.length; i++) {
tokenNames[i] = VOCABULARY.getLiteralName(i);
if (tokenNames[i] == null) {
tokenNames[i] = VOCABULARY.getSymbolicName(i);
}
if (tokenNames[i] == null) {
tokenNames[i] = "<INVALID>";
}
}
}
static {
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
@ -173,74 +246,4 @@ public class StupsLexer extends Lexer {
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
}
}
public StupsLexer(CharStream input) {
super(input);
_interp = new LexerATNSimulator(this, _ATN, _decisionToDFA, _sharedContextCache);
}
private static String[] makeRuleNames() {
return new String[]{
"FIRST_DIGIT", "DIGIT", "LOWERCASE", "UPPERCASE", "LETTER", "LETTER_DIGIT",
"WHITE", "ANY", "ANY_NOBREAK", "ANY_NOWHITE", "WHITESPACE", "MULTILINE_COMMENT",
"LINE_COMMENT", "CLASS", "PUBLIC", "STATIC", "VOID_TYPE", "BOOLEAN_TYPE",
"STRING_TYPE", "IF", "ELSE", "WHILE", "PRINTLN", "ASSIGN", "ADD", "SUB",
"MUL", "DIV", "MOD", "NOT", "AND", "OR", "EQUAL", "NOT_EQUAL", "LESS",
"LESS_EQUAL", "GREATER", "GREATER_EQUAL", "L_PAREN", "R_PAREN", "L_BRACE",
"R_BRACE", "L_BRACKET", "R_BRACKET", "SEMICOLON", "COMMA", "DOT", "INTEGER_LIT",
"STRING_LIT", "BOOLEAN_LIT", "IDENTIFIER"
};
}
private static String[] makeLiteralNames() {
return new String[]{
null, null, null, null, "'class'", "'public'", "'static'", "'void'",
"'boolean'", "'String'", "'if'", "'else'", "'while'", "'System.out.println'",
"'='", "'+'", "'-'", "'*'", "'/'", "'%'", "'!'", "'&&'", "'||'", "'=='",
"'!='", "'<'", "'<='", "'>'", "'>='", "'('", "')'", "'{'", "'}'", "'['",
"']'", "';'", "','", "'.'"
};
}
private static String[] makeSymbolicNames() {
return new String[]{
null, "WHITESPACE", "MULTILINE_COMMENT", "LINE_COMMENT", "CLASS", "PUBLIC",
"STATIC", "VOID_TYPE", "BOOLEAN_TYPE", "STRING_TYPE", "IF", "ELSE", "WHILE",
"PRINTLN", "ASSIGN", "ADD", "SUB", "MUL", "DIV", "MOD", "NOT", "AND",
"OR", "EQUAL", "NOT_EQUAL", "LESS", "LESS_EQUAL", "GREATER", "GREATER_EQUAL",
"L_PAREN", "R_PAREN", "L_BRACE", "R_BRACE", "L_BRACKET", "R_BRACKET",
"SEMICOLON", "COMMA", "DOT", "INTEGER_LIT", "STRING_LIT", "BOOLEAN_LIT",
"IDENTIFIER"
};
}
@Override
public String[] getRuleNames() { return ruleNames; }
@Override
public Vocabulary getVocabulary() {
return VOCABULARY;
}
@Override
public String getSerializedATN() { return _serializedATN; }
@Override
public String getGrammarFileName() { return "StupsLexer.g4"; }
@Override
public ATN getATN() { return _ATN; }
@Override
public String[] getChannelNames() { return channelNames; }
@Override
public String[] getModeNames() { return modeNames; }
@Override
@Deprecated
public String[] getTokenNames() {
return tokenNames;
}
}

View File

@ -1,16 +1,16 @@
package parser;
import java.util.List;
import java.util.Set;
public interface ILL1ParsingTable {
List<String> get(String nonterminal, String terminal);
String get(String nonterminal, String terminal);
String getStartSymbol();
List<String> getNonterminals();
Set<String> getNonterminals();
List<String> getTerminals();
Set<String> getTerminals();
String getEpsilon();
}

View File

@ -1,23 +1,320 @@
package parser;
import java.util.AbstractMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
public class LL1GrammarAnalyzer {
private final Set<String> nullable;
private final Map<String, Set<String>> first;
private final Map<String, Set<String>> follow;
private final ILL1ParsingTable table;
public LL1GrammarAnalyzer(Set<String> terminals, Set<String> nonterminals,
String start, String epsilon,
Map<String, List<String>> productions) {
// Es muss zwingend in der Reihenfolge [Nullable < First < Follow < Table] initialisiert werden
this.nullable = this.initNullable(productions, epsilon);
this.first = this.initFirst(productions, terminals, nonterminals, epsilon);
this.follow = this.initFollow(productions, terminals, nonterminals, epsilon);
this.table = this.initParseTable(productions, terminals, nonterminals, start, epsilon);
}
private Set<String> initNullable(Map<String, List<String>> productions, String epsilon) {
Set<String> nullableOut = new HashSet<>();
boolean change;
do {
change = false;
for (Map.Entry<String, List<String>> prods : productions.entrySet()) {
// Für jedes Nichtterminal
final String leftX = prods.getKey();
for (String prod : prods.getValue()) {
// Für jede Produktionsregel von diesem Nichtterminal
// Produktionsregel der Form X -> S1 S2 S3 ... Sk
final String[] split = prod.split(" ");
boolean allNullable = true; // Sind alle rechten Symbole nullable?
for (String rightSi : split) {
// Für jedes rechte Symbol dieser Produktionsregel
if (!(nullableOut.contains(rightSi) || rightSi.equals(epsilon))) {
allNullable = false;
break;
}
}
if (!(nullableOut.contains(leftX) || leftX.equals(epsilon)) && allNullable) {
// Alle rechten Symbole sind nullable, also ist X nullable
change = nullableOut.add(leftX);
}
}
}
} while (change);
return nullableOut;
}
public boolean nullable(String sym) {
return this.nullable.contains(sym);
}
public boolean stringNullable(String prod) {
for (String rightSi : prod.split(" ")) {
if (!this.nullable.contains(rightSi)) {
return false;
}
}
return true;
}
private Map<String, Set<String>> initFirst(Map<String, List<String>> productions,
Set<String> terminals, Set<String> nonterminals,
String epsilon) {
Map<String, Set<String>> firstOut = new HashMap<>();
boolean change;
for (String sym : nonterminals) {
// Alle Nichtterminale mit leeren Sets initialisieren
firstOut.put(sym, new HashSet<>());
}
for (String sym : terminals) {
// Alle Terminale mit der Identität initialisieren
firstOut.put(sym, new HashSet<>());
firstOut.get(sym).add(sym);
}
do {
change = false;
for (Map.Entry<String, List<String>> prods : productions.entrySet()) {
// Für jedes Nichtterminal
final String leftX = prods.getKey();
for (String prod : prods.getValue()) {
// Für jede Produktionsregel von diesem Nichtterminal
// Produktionsregel der Form X -> S1 S2 S3 ... Sk
if (prod.equals(epsilon)) {
// Epsilonregeln überspringen
continue;
}
final String[] split = prod.split(" ");
// Das First des linken Nichtterminals X enthält das first des ersten rechten Symbols dieser
// Produktionsregel S1 (da X -> S1 ... Sk)
firstOut.get(leftX).addAll(firstOut.get(split[0]));
for (int i = 1; i < split.length; i++) {
// Für das 2-te bis k-te rechte Symbol dieser Produktionsregel
final String sym = split[i];
if (this.nullable(split[i - 1])) {
change = firstOut.get(leftX).addAll(firstOut.get(sym));
} else {
break;
}
}
}
}
} while (change);
return firstOut;
}
public Set<String> first(String sym) {
return this.first.get(sym);
}
public Set<String> stringFirst(String prod) {
if (prod.isEmpty()) {
return Collections.emptySet();
}
String front;
String rest;
if (prod.indexOf(' ') < 0) {
front = prod;
rest = "";
} else {
front = prod.substring(0, prod.indexOf(' '));
rest = prod.substring(prod.indexOf(' ') + 1);
}
Set<String> firstOut = new HashSet<>(this.first(front));
if (this.nullable(front)) {
firstOut.addAll(this.stringFirst(rest));
}
return firstOut;
}
private Map<String, Set<String>> initFollow(Map<String, List<String>> productions,
Set<String> terminals, Set<String> nonterminals,
String epsilon) {
Map<String, Set<String>> followOut = new HashMap<>();
boolean change;
for (String sym : terminals) {
// Alle Nichtterminale mit leeren Sets initialisieren
followOut.put(sym, new HashSet<>());
}
for (String sym : nonterminals) {
// Alle Terminale mit leeren Sets initialisieren
followOut.put(sym, new HashSet<>());
}
do {
change = false;
for (Map.Entry<String, List<String>> prods : productions.entrySet()) {
// Für jedes Nichtterminal
final String leftX = prods.getKey();
for (String prod : prods.getValue()) {
// Für jede Produktionsregel von diesem Nichtterminal
// Produktionsregel der Form X -> S1 S2 S3 ... Sk
final String[] split = prod.split(" ");
for (int i = 0; i < split.length - 1; i++) {
// Für das 1-te bis vorletzte rechte Symbol dieser Produktionsregel
final String sym = split[i];
// Das follow des i-ten rechten Symbols dieser Produktionsregel enthält das first des
// (i+1)-ten rechten Sybols dieser Produktionsregel
followOut.get(sym).addAll(this.first(split[i + 1]));
for (int j = i + 2; j < prods.getValue().size(); j++) {
// Für das (i+2)-te bis letzte rechte Symbol dieser Produktionsregel
boolean allNullable = true; // Sind alle rechten Symbole nullable?
for (int k = i + 1; k < j; k++) {
// Für das (i+1)-te bis letzte rechte Symbol dieser Produktionsregel
if (!this.nullable(split[k])) {
allNullable = false;
break;
}
}
if (allNullable) {
// Alle zwischen dem (i+1)-ten und j-ten rechten Symbol dieser Produktionsregel sind
// nullable, deshalb enthält follow(Si) auch follow(Sj)
change = followOut.get(sym).addAll(this.first(split[j]));
}
}
boolean allNullable = true; // Sind alle rechten Symbole nullable?
for (int k = i + 1; k < split.length; k++) {
// Für das (i+1)-te bis letzte rechte Symbol dieser Produktionsregel
if (!this.nullable(split[k])) {
allNullable = false;
break;
}
}
if (allNullable) {
// Alle zwischen dem (i+1)-ten bis letzten rechten Symbol dieser Produktionsregel sind
// nullable, deshalb enthält follow(Si) auch follow(X)
change = followOut.get(sym).addAll(followOut.get(leftX));
}
}
// Dem letzten rechten Symbol dieser Produktionsregel wird das follow des linken Nichtterminals
// hinzugefügt: follow(Sk) enthält follow(X) (da X -> S1 ... Sk)
if (!split[split.length - 1].equals(epsilon)) {
//Epsilonregeln überspringen
followOut.get(split[split.length - 1]).addAll(followOut.get(leftX));
}
}
}
} while (change);
return followOut;
}
public Set<String> follow(String sym) {
return this.follow.get(sym);
}
private ILL1ParsingTable initParseTable(Map<String, List<String>> productions,
Set<String> terminals, Set<String> nonterminals,
String start, String epsilon) {
Map<Map.Entry<String, String>, String> parseTableOut = new HashMap<>();
for (String leftX : nonterminals) {
// Für alle Nichtterminale (Zeilen der Tabelle)
for (String terminal : terminals) {
// Für alle Terminale (Spalten der Tabelle)
final Map.Entry<String, String> cell = new AbstractMap.SimpleEntry<>(leftX, terminal);
for (String prod : productions.get(leftX)) {
// Für jede Produktionsregel für dieses Nichtterminal
if (prod.equals(epsilon)) {
// Epsilonregeln überspringen
continue;
}
if (this.stringFirst(prod).contains(terminal)
|| (this.stringNullable(prod) && this.follow(leftX).contains(terminal))) {
// Verwende Produktion X -> S1 ... Sk, wenn Eingabe c in first(S1 ... Sk) ist
// oder nullable(S1 ... Sk) und Eingabe c in follow(X) ist
parseTableOut.put(cell, prod);
}
}
}
}
return new LL1ParsingTable(nonterminals, terminals, start, epsilon, parseTableOut);
}
public Set<String> getNullable() {
return null;
return this.nullable;
}
public Map<String, Set<String>> getFirst() {
return null;
return this.first;
}
public Map<String, Set<String>> getFollow() {
return null;
return this.follow;
}
public ILL1ParsingTable getTable() {
return null;
return this.table;
}
}

View File

@ -26,34 +26,41 @@ public class LL1Parser {
// Parsing
while (!stack.isEmpty()) {
String top = stack.peek().getName();
final String top = stack.peek().getName();
if (currentToken >= token.size()) {
// Wenn auf dem Stack mehr Nichtterminale liegen als Terminale in der Eingabe vorhanden sind
throw new MyParseException("Input too long");
}
List<String> prod = this.parsetable.get(top, token.get(currentToken));
final String prod = this.parsetable.get(top, token.get(currentToken));
if (top.equals(token.get(currentToken))) {
// Wenn auf dem Stack ein Terminal liegt
stack.pop();
currentToken++;
} else if (this.parsetable.getTerminals().contains(top)) {
// Wenn das Terminal auf dem Stack nicht mit der aktuellen Eingabe übereinstimmt
throw new MyParseException("Invalid terminal on stack: " + top);
} else if (prod == null) {
// Wenn es für das aktuelle Terminal und das Nichtterminal auf dem Stack keine Regel gibt
throw new MyParseException("No prod. for nonterminal " + top + ", terminal " + token.get(currentToken));
} else {
// Wenn das Nichtterminal auf dem Stack durch (s)eine Produktion ersetzt werden kann
// Hier wird auch der AST aufgebaut
final String[] split = prod.split(" ");
System.out.println(top + " -> " + prod);
Node pop = stack.pop();
for (int i = prod.size() - 1; i >= 0; i--) {
Node node = new Node(prod.get(i));
for (int i = split.length - 1; i >= 0; i--) {
Node node = new Node(split[i]);
stack.push(node);
pop.addChild(node);
}

View File

@ -1,23 +1,23 @@
package parser;
import java.util.AbstractMap.SimpleEntry;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
public class LL1ParsingTable implements ILL1ParsingTable {
private final String start;
private final List<String> terminals;
private final List<String> nonterminals;
private final Set<String> terminals;
private final Set<String> nonterminals;
private final String epsilon;
private final Map<Entry<String, String>, ? extends List<String>> parsetable;
private final Map<Entry<String, String>, String> parsetable;
public LL1ParsingTable(List<String> nonterminals,
List<String> terminals,
public LL1ParsingTable(Set<String> nonterminals,
Set<String> terminals,
String start,
String epsilon,
Map<Entry<String, String>, ? extends List<String>> parsetable) {
Map<Entry<String, String>, String> parsetable) {
this.start = start;
this.terminals = terminals;
this.nonterminals = nonterminals;
@ -26,7 +26,7 @@ public class LL1ParsingTable implements ILL1ParsingTable {
}
@Override
public List<String> get(String nonterminal, String terminal) {
public String get(String nonterminal, String terminal) {
return this.parsetable.get(new SimpleEntry<>(nonterminal, terminal));
}
@ -36,12 +36,12 @@ public class LL1ParsingTable implements ILL1ParsingTable {
}
@Override
public List<String> getNonterminals() {
public Set<String> getNonterminals() {
return this.nonterminals;
}
@Override
public List<String> getTerminals() {
public Set<String> getTerminals() {
return this.terminals;
}

View File

@ -6,8 +6,9 @@ import org.junit.jupiter.api.Test;
import java.util.AbstractMap.SimpleEntry;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
@ -29,25 +30,25 @@ class LL1ParserTest {
S -> i E t S
E -> b
*/
List<String> nonterminals;
Set<String> nonterminals;
String[] narray = {"S", "E"};
nonterminals = Arrays.asList(narray);
nonterminals = new HashSet<>(Arrays.asList(narray));
List<String> terminals;
Set<String> terminals;
String[] tarray = {"a", "b", "e", "i", "t"};
terminals = Arrays.asList(tarray);
terminals = new HashSet<>(Arrays.asList(tarray));
String startSymbol = "S";
String epsilonSymbol = "epsilon";
Map<Map.Entry<String, String>, List<String>> map;
Map<Map.Entry<String, String>, String> map;
map = new HashMap<>();
String[] production0 = {"a"};
map.put(new SimpleEntry<>("S", "a"), Arrays.asList(production0));
String[] production1 = {"i", "E", "t", "S"};
map.put(new SimpleEntry<>("S", "i"), Arrays.asList(production1));
String[] production2 = {"b"};
map.put(new SimpleEntry<>("E", "b"), Arrays.asList(production2));
String production0 = "a";
map.put(new SimpleEntry<>("S", "a"), production0);
String production1 = "i E t S";
map.put(new SimpleEntry<>("S", "i"), production1);
String production2 = "b";
map.put(new SimpleEntry<>("E", "b"), production2);
return new LL1ParsingTable(nonterminals, terminals, startSymbol, epsilonSymbol, map);
}
@ -56,45 +57,45 @@ class LL1ParserTest {
/*
Folie 4b/32
*/
List<String> nonterminals;
Set<String> nonterminals;
String[] narray = {"E", "T", "E2", "T2", "F"};
nonterminals = Arrays.asList(narray);
nonterminals = new HashSet<>(Arrays.asList(narray));
List<String> terminals;
Set<String> terminals;
String[] tarray = {"id", "+", "*", "(", ")"};
terminals = Arrays.asList(tarray);
terminals = new HashSet<>(Arrays.asList(tarray));
String startSymbol = "E";
String epsilonSymbol = "epsilon";
Map<Map.Entry<String, String>, List<String>> map;
Map<Map.Entry<String, String>, String> map;
map = new HashMap<>();
String[] production0 = {"T", "E2"};
map.put(new SimpleEntry<>("E", "id"), Arrays.asList(production0));
String[] production1 = {"T", "E2"};
map.put(new SimpleEntry<>("E", "("), Arrays.asList(production1));
String[] production2 = {"+", "T", "E2"};
map.put(new SimpleEntry<>("E2", "+"), Arrays.asList(production2));
String[] production3 = {"epsilon"};
map.put(new SimpleEntry<>("E2", ")"), Arrays.asList(production3));
String[] production4 = {"epsilon"};
map.put(new SimpleEntry<>("E2", "$"), Arrays.asList(production4));
String[] production5 = {"F", "T2"};
map.put(new SimpleEntry<>("T", "id"), Arrays.asList(production5));
String[] production6 = {"F", "T2"};
map.put(new SimpleEntry<>("T", "("), Arrays.asList(production6));
String[] production7 = {"epsilon"};
map.put(new SimpleEntry<>("T2", "+"), Arrays.asList(production7));
String[] production8 = {"*", "F", "T2"};
map.put(new SimpleEntry<>("T2", "*"), Arrays.asList(production8));
String[] production9 = {"epsilon"};
map.put(new SimpleEntry<>("T2", ")"), Arrays.asList(production9));
String[] production10 = {"epsilon"};
map.put(new SimpleEntry<>("T2", "$"), Arrays.asList(production10));
String[] production11 = {"id"};
map.put(new SimpleEntry<>("F", "id"), Arrays.asList(production11));
String[] production12 = {"(", "E", ")"};
map.put(new SimpleEntry<>("F", "("), Arrays.asList(production12));
String production0 = "T E2";
map.put(new SimpleEntry<>("E", "id"), production0);
String production1 = "T E2";
map.put(new SimpleEntry<>("E", "("), production1);
String production2 = "+ T E2";
map.put(new SimpleEntry<>("E2", "+"), production2);
String production3 = "epsilon";
map.put(new SimpleEntry<>("E2", ")"), production3);
String production4 = "epsilon";
map.put(new SimpleEntry<>("E2", "$"), production4);
String production5 = "F T2";
map.put(new SimpleEntry<>("T", "id"), production5);
String production6 = "F T2";
map.put(new SimpleEntry<>("T", "("), production6);
String production7 = "epsilon";
map.put(new SimpleEntry<>("T2", "+"), production7);
String production8 = "* F T2";
map.put(new SimpleEntry<>("T2", "*"), production8);
String production9 = "epsilon";
map.put(new SimpleEntry<>("T2", ")"), production9);
String production10 = "epsilon";
map.put(new SimpleEntry<>("T2", "$"), production10);
String production11 = "id";
map.put(new SimpleEntry<>("F", "id"), production11);
String production12 = "( E )";
map.put(new SimpleEntry<>("F", "("), production12);
return new LL1ParsingTable(nonterminals, terminals, startSymbol, epsilonSymbol, map);
}