implement grammaranalyzer
This commit is contained in:
@ -16,6 +16,10 @@ import org.antlr.v4.runtime.dfa.DFA;
|
|||||||
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
|
||||||
public class StupsLexer extends Lexer {
|
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
|
public static final int
|
||||||
WHITESPACE = 1, MULTILINE_COMMENT = 2, LINE_COMMENT = 3, CLASS = 4, PUBLIC = 5,
|
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,
|
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,
|
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,
|
L_BRACKET = 33, R_BRACKET = 34, SEMICOLON = 35, COMMA = 36, DOT = 37, INTEGER_LIT = 38,
|
||||||
STRING_LIT = 39, BOOLEAN_LIT = 40, IDENTIFIER = 41;
|
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 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 Use {@link #VOCABULARY} instead.
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
@Deprecated
|
||||||
public static final String[] tokenNames;
|
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 =
|
public static final String _serializedATN =
|
||||||
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2+\u0143\b\1\4\2\t" +
|
"\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" +
|
"\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";
|
"\u0140\3\b\2\2";
|
||||||
public static final ATN _ATN =
|
public static final ATN _ATN =
|
||||||
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
|
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 {
|
static {
|
||||||
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
|
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
|
||||||
@ -173,74 +246,4 @@ public class StupsLexer extends Lexer {
|
|||||||
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
|
_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;
|
package parser;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
|
|
||||||
public interface ILL1ParsingTable {
|
public interface ILL1ParsingTable {
|
||||||
|
|
||||||
List<String> get(String nonterminal, String terminal);
|
String get(String nonterminal, String terminal);
|
||||||
|
|
||||||
String getStartSymbol();
|
String getStartSymbol();
|
||||||
|
|
||||||
List<String> getNonterminals();
|
Set<String> getNonterminals();
|
||||||
|
|
||||||
List<String> getTerminals();
|
Set<String> getTerminals();
|
||||||
|
|
||||||
String getEpsilon();
|
String getEpsilon();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,320 @@
|
|||||||
package parser;
|
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.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public class LL1GrammarAnalyzer {
|
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() {
|
public Set<String> getNullable() {
|
||||||
return null;
|
return this.nullable;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Set<String>> getFirst() {
|
public Map<String, Set<String>> getFirst() {
|
||||||
return null;
|
return this.first;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<String, Set<String>> getFollow() {
|
public Map<String, Set<String>> getFollow() {
|
||||||
return null;
|
return this.follow;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ILL1ParsingTable getTable() {
|
public ILL1ParsingTable getTable() {
|
||||||
return null;
|
return this.table;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,34 +26,41 @@ public class LL1Parser {
|
|||||||
|
|
||||||
// Parsing
|
// Parsing
|
||||||
while (!stack.isEmpty()) {
|
while (!stack.isEmpty()) {
|
||||||
String top = stack.peek().getName();
|
final String top = stack.peek().getName();
|
||||||
|
|
||||||
if (currentToken >= token.size()) {
|
if (currentToken >= token.size()) {
|
||||||
// Wenn auf dem Stack mehr Nichtterminale liegen als Terminale in der Eingabe vorhanden sind
|
// Wenn auf dem Stack mehr Nichtterminale liegen als Terminale in der Eingabe vorhanden sind
|
||||||
|
|
||||||
throw new MyParseException("Input too long");
|
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))) {
|
if (top.equals(token.get(currentToken))) {
|
||||||
// Wenn auf dem Stack ein Terminal liegt
|
// Wenn auf dem Stack ein Terminal liegt
|
||||||
|
|
||||||
stack.pop();
|
stack.pop();
|
||||||
currentToken++;
|
currentToken++;
|
||||||
|
|
||||||
} else if (this.parsetable.getTerminals().contains(top)) {
|
} else if (this.parsetable.getTerminals().contains(top)) {
|
||||||
// Wenn das Terminal auf dem Stack nicht mit der aktuellen Eingabe übereinstimmt
|
// Wenn das Terminal auf dem Stack nicht mit der aktuellen Eingabe übereinstimmt
|
||||||
|
|
||||||
throw new MyParseException("Invalid terminal on stack: " + top);
|
throw new MyParseException("Invalid terminal on stack: " + top);
|
||||||
|
|
||||||
} else if (prod == null) {
|
} else if (prod == null) {
|
||||||
// Wenn es für das aktuelle Terminal und das Nichtterminal auf dem Stack keine Regel gibt
|
// 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));
|
throw new MyParseException("No prod. for nonterminal " + top + ", terminal " + token.get(currentToken));
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 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
|
||||||
|
|
||||||
|
final String[] split = prod.split(" ");
|
||||||
|
|
||||||
System.out.println(top + " -> " + prod);
|
System.out.println(top + " -> " + prod);
|
||||||
Node pop = stack.pop();
|
Node pop = stack.pop();
|
||||||
for (int i = prod.size() - 1; i >= 0; i--) {
|
for (int i = split.length - 1; i >= 0; i--) {
|
||||||
Node node = new Node(prod.get(i));
|
Node node = new Node(split[i]);
|
||||||
stack.push(node);
|
stack.push(node);
|
||||||
pop.addChild(node);
|
pop.addChild(node);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +1,23 @@
|
|||||||
package parser;
|
package parser;
|
||||||
|
|
||||||
import java.util.AbstractMap.SimpleEntry;
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class LL1ParsingTable implements ILL1ParsingTable {
|
public class LL1ParsingTable implements ILL1ParsingTable {
|
||||||
|
|
||||||
private final String start;
|
private final String start;
|
||||||
private final List<String> terminals;
|
private final Set<String> terminals;
|
||||||
private final List<String> nonterminals;
|
private final Set<String> nonterminals;
|
||||||
private final String epsilon;
|
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,
|
public LL1ParsingTable(Set<String> nonterminals,
|
||||||
List<String> terminals,
|
Set<String> terminals,
|
||||||
String start,
|
String start,
|
||||||
String epsilon,
|
String epsilon,
|
||||||
Map<Entry<String, String>, ? extends List<String>> parsetable) {
|
Map<Entry<String, String>, String> parsetable) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
this.terminals = terminals;
|
this.terminals = terminals;
|
||||||
this.nonterminals = nonterminals;
|
this.nonterminals = nonterminals;
|
||||||
@ -26,7 +26,7 @@ public class LL1ParsingTable implements ILL1ParsingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> get(String nonterminal, String terminal) {
|
public String get(String nonterminal, String terminal) {
|
||||||
return this.parsetable.get(new SimpleEntry<>(nonterminal, terminal));
|
return this.parsetable.get(new SimpleEntry<>(nonterminal, terminal));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,12 +36,12 @@ public class LL1ParsingTable implements ILL1ParsingTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getNonterminals() {
|
public Set<String> getNonterminals() {
|
||||||
return this.nonterminals;
|
return this.nonterminals;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getTerminals() {
|
public Set<String> getTerminals() {
|
||||||
return this.terminals;
|
return this.terminals;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -6,8 +6,9 @@ import org.junit.jupiter.api.Test;
|
|||||||
import java.util.AbstractMap.SimpleEntry;
|
import java.util.AbstractMap.SimpleEntry;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||||
@ -29,25 +30,25 @@ class LL1ParserTest {
|
|||||||
S -> i E t S
|
S -> i E t S
|
||||||
E -> b
|
E -> b
|
||||||
*/
|
*/
|
||||||
List<String> nonterminals;
|
Set<String> nonterminals;
|
||||||
String[] narray = {"S", "E"};
|
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"};
|
String[] tarray = {"a", "b", "e", "i", "t"};
|
||||||
terminals = Arrays.asList(tarray);
|
terminals = new HashSet<>(Arrays.asList(tarray));
|
||||||
|
|
||||||
String startSymbol = "S";
|
String startSymbol = "S";
|
||||||
String epsilonSymbol = "epsilon";
|
String epsilonSymbol = "epsilon";
|
||||||
|
|
||||||
Map<Map.Entry<String, String>, List<String>> map;
|
Map<Map.Entry<String, String>, String> map;
|
||||||
map = new HashMap<>();
|
map = new HashMap<>();
|
||||||
String[] production0 = {"a"};
|
String production0 = "a";
|
||||||
map.put(new SimpleEntry<>("S", "a"), Arrays.asList(production0));
|
map.put(new SimpleEntry<>("S", "a"), production0);
|
||||||
String[] production1 = {"i", "E", "t", "S"};
|
String production1 = "i E t S";
|
||||||
map.put(new SimpleEntry<>("S", "i"), Arrays.asList(production1));
|
map.put(new SimpleEntry<>("S", "i"), production1);
|
||||||
String[] production2 = {"b"};
|
String production2 = "b";
|
||||||
map.put(new SimpleEntry<>("E", "b"), Arrays.asList(production2));
|
map.put(new SimpleEntry<>("E", "b"), production2);
|
||||||
|
|
||||||
return new LL1ParsingTable(nonterminals, terminals, startSymbol, epsilonSymbol, map);
|
return new LL1ParsingTable(nonterminals, terminals, startSymbol, epsilonSymbol, map);
|
||||||
}
|
}
|
||||||
@ -56,45 +57,45 @@ class LL1ParserTest {
|
|||||||
/*
|
/*
|
||||||
Folie 4b/32
|
Folie 4b/32
|
||||||
*/
|
*/
|
||||||
List<String> nonterminals;
|
Set<String> nonterminals;
|
||||||
String[] narray = {"E", "T", "E2", "T2", "F"};
|
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", "+", "*", "(", ")"};
|
String[] tarray = {"id", "+", "*", "(", ")"};
|
||||||
terminals = Arrays.asList(tarray);
|
terminals = new HashSet<>(Arrays.asList(tarray));
|
||||||
|
|
||||||
String startSymbol = "E";
|
String startSymbol = "E";
|
||||||
String epsilonSymbol = "epsilon";
|
String epsilonSymbol = "epsilon";
|
||||||
|
|
||||||
Map<Map.Entry<String, String>, List<String>> map;
|
Map<Map.Entry<String, String>, String> map;
|
||||||
map = new HashMap<>();
|
map = new HashMap<>();
|
||||||
String[] production0 = {"T", "E2"};
|
String production0 = "T E2";
|
||||||
map.put(new SimpleEntry<>("E", "id"), Arrays.asList(production0));
|
map.put(new SimpleEntry<>("E", "id"), production0);
|
||||||
String[] production1 = {"T", "E2"};
|
String production1 = "T E2";
|
||||||
map.put(new SimpleEntry<>("E", "("), Arrays.asList(production1));
|
map.put(new SimpleEntry<>("E", "("), production1);
|
||||||
String[] production2 = {"+", "T", "E2"};
|
String production2 = "+ T E2";
|
||||||
map.put(new SimpleEntry<>("E2", "+"), Arrays.asList(production2));
|
map.put(new SimpleEntry<>("E2", "+"), production2);
|
||||||
String[] production3 = {"epsilon"};
|
String production3 = "epsilon";
|
||||||
map.put(new SimpleEntry<>("E2", ")"), Arrays.asList(production3));
|
map.put(new SimpleEntry<>("E2", ")"), production3);
|
||||||
String[] production4 = {"epsilon"};
|
String production4 = "epsilon";
|
||||||
map.put(new SimpleEntry<>("E2", "$"), Arrays.asList(production4));
|
map.put(new SimpleEntry<>("E2", "$"), production4);
|
||||||
String[] production5 = {"F", "T2"};
|
String production5 = "F T2";
|
||||||
map.put(new SimpleEntry<>("T", "id"), Arrays.asList(production5));
|
map.put(new SimpleEntry<>("T", "id"), production5);
|
||||||
String[] production6 = {"F", "T2"};
|
String production6 = "F T2";
|
||||||
map.put(new SimpleEntry<>("T", "("), Arrays.asList(production6));
|
map.put(new SimpleEntry<>("T", "("), production6);
|
||||||
String[] production7 = {"epsilon"};
|
String production7 = "epsilon";
|
||||||
map.put(new SimpleEntry<>("T2", "+"), Arrays.asList(production7));
|
map.put(new SimpleEntry<>("T2", "+"), production7);
|
||||||
String[] production8 = {"*", "F", "T2"};
|
String production8 = "* F T2";
|
||||||
map.put(new SimpleEntry<>("T2", "*"), Arrays.asList(production8));
|
map.put(new SimpleEntry<>("T2", "*"), production8);
|
||||||
String[] production9 = {"epsilon"};
|
String production9 = "epsilon";
|
||||||
map.put(new SimpleEntry<>("T2", ")"), Arrays.asList(production9));
|
map.put(new SimpleEntry<>("T2", ")"), production9);
|
||||||
String[] production10 = {"epsilon"};
|
String production10 = "epsilon";
|
||||||
map.put(new SimpleEntry<>("T2", "$"), Arrays.asList(production10));
|
map.put(new SimpleEntry<>("T2", "$"), production10);
|
||||||
String[] production11 = {"id"};
|
String production11 = "id";
|
||||||
map.put(new SimpleEntry<>("F", "id"), Arrays.asList(production11));
|
map.put(new SimpleEntry<>("F", "id"), production11);
|
||||||
String[] production12 = {"(", "E", ")"};
|
String production12 = "( E )";
|
||||||
map.put(new SimpleEntry<>("F", "("), Arrays.asList(production12));
|
map.put(new SimpleEntry<>("F", "("), production12);
|
||||||
|
|
||||||
return new LL1ParsingTable(nonterminals, terminals, startSymbol, epsilonSymbol, map);
|
return new LL1ParsingTable(nonterminals, terminals, startSymbol, epsilonSymbol, map);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user