implement grammaranalyzer
This commit is contained in:
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user