implement ll1-nonrecursive-descent-parser
This commit is contained in:
16
src/main/java/parser/ILL1ParsingTable.java
Normal file
16
src/main/java/parser/ILL1ParsingTable.java
Normal file
@ -0,0 +1,16 @@
|
||||
package parser;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface ILL1ParsingTable {
|
||||
|
||||
List<String> get(String nonterminal, String terminal);
|
||||
|
||||
String getStartSymbol();
|
||||
|
||||
List<String> getNonterminals();
|
||||
|
||||
List<String> getTerminals();
|
||||
|
||||
String getEpsilon();
|
||||
}
|
52
src/main/java/parser/LL1Parser.java
Normal file
52
src/main/java/parser/LL1Parser.java
Normal file
@ -0,0 +1,52 @@
|
||||
package parser;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
public class LL1Parser {
|
||||
|
||||
private final ILL1ParsingTable parsetable;
|
||||
|
||||
public LL1Parser(ILL1ParsingTable parsetable) {
|
||||
this.parsetable = parsetable;
|
||||
}
|
||||
|
||||
public boolean parse(List<String> token) {
|
||||
Deque<String> stack = new ArrayDeque<>();
|
||||
stack.push(this.parsetable.getStartSymbol());
|
||||
|
||||
int current = 0;
|
||||
System.out.println("\nParsing " + token + ":");
|
||||
|
||||
while (!stack.isEmpty()) {
|
||||
String top = stack.peek();
|
||||
|
||||
// Wenn auf dem Stack mehr Nichtterminale liegen als Terminale in der Eingabe vorhanden sind
|
||||
if (current >= token.size()) {
|
||||
throw new MyParseException("Input too long");
|
||||
}
|
||||
List<String> prod = this.parsetable.get(top, token.get(current));
|
||||
|
||||
if (top.equals(token.get(current))) {
|
||||
stack.pop();
|
||||
current++;
|
||||
|
||||
} else if (this.parsetable.getTerminals().contains(top)) {
|
||||
throw new MyParseException("Invalid terminal on stack: " + top);
|
||||
|
||||
} else if (prod == null) {
|
||||
throw new MyParseException("No prod. for nonterminal " + top + " and terminal " + token.get(current));
|
||||
|
||||
} else {
|
||||
System.out.println(top + " -> " + prod);
|
||||
stack.pop();
|
||||
for (int i = prod.size() - 1; i >= 0; i--) {
|
||||
stack.push(prod.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
52
src/main/java/parser/LL1ParsingTable.java
Normal file
52
src/main/java/parser/LL1ParsingTable.java
Normal file
@ -0,0 +1,52 @@
|
||||
package parser;
|
||||
|
||||
import java.util.AbstractMap.SimpleEntry;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public class LL1ParsingTable implements ILL1ParsingTable {
|
||||
|
||||
private final String start;
|
||||
private final List<String> terminals;
|
||||
private final List<String> nonterminals;
|
||||
private final String epsilon;
|
||||
private final Map<Entry<String, String>, ? extends List<String>> parsetable;
|
||||
|
||||
public LL1ParsingTable(List<String> nonterminals,
|
||||
List<String> terminals,
|
||||
String start,
|
||||
String epsilon,
|
||||
Map<Entry<String, String>, ? extends List<String>> parsetable) {
|
||||
this.start = start;
|
||||
this.terminals = terminals;
|
||||
this.nonterminals = nonterminals;
|
||||
this.epsilon = epsilon;
|
||||
this.parsetable = parsetable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> get(String nonterminal, String terminal) {
|
||||
return this.parsetable.get(new SimpleEntry<>(nonterminal, terminal));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getStartSymbol() {
|
||||
return this.start;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getNonterminals() {
|
||||
return this.nonterminals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTerminals() {
|
||||
return this.terminals;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEpsilon() {
|
||||
return this.epsilon;
|
||||
}
|
||||
}
|
8
src/main/java/parser/MyParseException.java
Normal file
8
src/main/java/parser/MyParseException.java
Normal file
@ -0,0 +1,8 @@
|
||||
package parser;
|
||||
|
||||
public class MyParseException extends RuntimeException {
|
||||
|
||||
public MyParseException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
151
src/test/java/parser/LL1ParserTest.java
Normal file
151
src/test/java/parser/LL1ParserTest.java
Normal file
@ -0,0 +1,151 @@
|
||||
package parser;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
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.Map;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
|
||||
class LL1ParserTest {
|
||||
|
||||
private static ILL1ParsingTable table0;
|
||||
private static ILL1ParsingTable table1;
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() {
|
||||
table0 = initTable0();
|
||||
table1 = initTable1();
|
||||
}
|
||||
|
||||
private static ILL1ParsingTable initTable0() {
|
||||
/*
|
||||
S -> a
|
||||
S -> i E t S
|
||||
E -> b
|
||||
*/
|
||||
List<String> nonterminals;
|
||||
String[] narray = {"S", "E"};
|
||||
nonterminals = Arrays.asList(narray);
|
||||
|
||||
List<String> terminals;
|
||||
String[] tarray = {"a", "b", "e", "i", "t"};
|
||||
terminals = Arrays.asList(tarray);
|
||||
|
||||
String startSymbol = "S";
|
||||
String epsilonSymbol = "epsilon";
|
||||
|
||||
Map<Map.Entry<String, String>, List<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));
|
||||
|
||||
return new LL1ParsingTable(nonterminals, terminals, startSymbol, epsilonSymbol, map);
|
||||
}
|
||||
|
||||
private static ILL1ParsingTable initTable1() {
|
||||
/*
|
||||
Folie 4b/32
|
||||
*/
|
||||
List<String> nonterminals;
|
||||
String[] narray = {"E", "T", "E2", "T2", "F"};
|
||||
nonterminals = Arrays.asList(narray);
|
||||
|
||||
List<String> terminals;
|
||||
String[] tarray = {"id", "+", "*", "(", ")"};
|
||||
terminals = Arrays.asList(tarray);
|
||||
|
||||
String startSymbol = "E";
|
||||
String epsilonSymbol = "epsilon";
|
||||
|
||||
Map<Map.Entry<String, String>, List<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));
|
||||
|
||||
return new LL1ParsingTable(nonterminals, terminals, startSymbol, epsilonSymbol, map);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testIfThenElse() throws MyParseException {
|
||||
LL1Parser parser = new LL1Parser(table0);
|
||||
|
||||
String[] token1 = {"i", "b", "t", "a"};
|
||||
String[] token2 = {"i", "b", "t", "i", "b", "t", "a"};
|
||||
String[] token3 = {"i", "b", "t", "i", "b", "t", "i", "b", "t", "a"};
|
||||
|
||||
assertThat(parser.parse(Arrays.asList(token1))).isTrue();
|
||||
assertThat(parser.parse(Arrays.asList(token2))).isTrue();
|
||||
assertThat(parser.parse(Arrays.asList(token3))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testException0() {
|
||||
LL1Parser parser = new LL1Parser(table0);
|
||||
String[] token1 = {"i", "b", "t"};
|
||||
|
||||
assertThatThrownBy(() -> parser.parse(Arrays.asList(token1))).isInstanceOf(MyParseException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testException1() {
|
||||
LL1Parser parser = new LL1Parser(table0);
|
||||
String[] token1 = {"i", "b", "t", "t"};
|
||||
|
||||
assertThatThrownBy(() -> parser.parse(Arrays.asList(token1))).isInstanceOf(MyParseException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testArithExpression() {
|
||||
LL1Parser parser = new LL1Parser(table1);
|
||||
|
||||
String[] token1 = {"id", "+", "id", "*", "id"};
|
||||
String[] token2 = {"id", "*", "id", "*", "id"};
|
||||
String[] token3 = {"id", "+", "id"};
|
||||
|
||||
assertThatThrownBy(() -> parser.parse(Arrays.asList(token3))).isInstanceOf(MyParseException.class);
|
||||
assertThatThrownBy(() -> parser.parse(Arrays.asList(token3))).isInstanceOf(MyParseException.class);
|
||||
assertThatThrownBy(() -> parser.parse(Arrays.asList(token3))).isInstanceOf(MyParseException.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void testException2() {
|
||||
LL1Parser parser = new LL1Parser(table1);
|
||||
String[] token1 = {"id", "id"};
|
||||
|
||||
assertThatThrownBy(() -> parser.parse(Arrays.asList(token1))).isInstanceOf(MyParseException.class);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user