1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
package scanner;
import java.util.HashMap;
import java.util.Stack;
public class Scanner {
HashMap<Character, String> charCategories;
HashMap<String, HashMap<String, String>> transitions;
HashMap<String, String> tokenTypes;
String s0;
/**
* Builds the tables needed for the scanner.
*/
public Scanner(TableReader tableReader) {
charCategories = new HashMap<>();
transitions = new HashMap<>();
tokenTypes = new HashMap<>();
// Build catMap, mapping a character to a category.
for (TableReader.CharCat cat : tableReader.getClassifier())
charCategories.put(cat.getC(), cat.getCategory());
// Build the transition table. Given a state and a character category,
// give a new state.
for (TableReader.Transition t : tableReader.getTransitions()) {
if (s0 == null)
s0 = t.getFromStateName();
if (!transitions.containsKey(t.getFromStateName()))
transitions.put(t.getFromStateName(), new HashMap<String, String>());
transitions.get(t.getFromStateName())
.put(t.getCategory(), t.getToStateName());
}
// Build the token types table
for (TableReader.TokenType tt : tableReader.getTokens())
tokenTypes.put(tt.getState(), tt.getType());
}
/**
* Returns the category for c or "not in alphabet" if c has no category. Do
* not hardcode this. That is, this function should have nothing more than a
* table lookup or two. You should not have any character literals in here
* such as 'r' or '3'.
*/
public String getCategory(Character c) {
if (charCategories.containsKey(c))
return charCategories.get(c);
return "not in alphabet";
}
/**
* Returns the new state given a current state and category. This models
* the transition table. Returns "error" if there is no transition.
* Do not hardcode any state names or categories. You should have only
* table lookups here.
*/
public String getNewState(String state, String category) {
if (transitionExists(state, category))
return transitions.get(state).get(category);
return "error";
}
private boolean transitionExists(String state, String category) {
if (transitions.containsKey(state)) {
HashMap<String, String> transitionTable = transitions.get(state);
return transitionTable.containsKey(category);
}
return false;
}
/**
* Returns the type of token corresponding to a given state. If the state
* is not accepting then return "error".
* Do not hardcode any state names or token types.
*/
public String getTokenType(String state) {
if (tokenTypes.containsKey(state))
return tokenTypes.get(state);
return "error";
}
/**
* Return the next token or null if there's a lexical error.
*/
public Token nextToken(ScanStream ss) {
String state = s0;
String lexeme = "";
Stack<String> stack = new Stack<>();
stack.push("bad");
while (!state.equals("error") && !ss.eof()) {
char c = ss.next();
lexeme += c;
if (tokenTypes.containsKey(state))
stack.clear();
stack.push(state);
state = getNewState(state, getCategory(c));
}
while (!tokenTypes.containsKey(state) && !state.equals("bad")) {
state = stack.pop();
if (lexeme.length() > 0)
lexeme = lexeme.substring(0, lexeme.length() - 1);
ss.rollback();
}
if (tokenTypes.containsKey(state))
return new Token(tokenTypes.get(state), lexeme);
return null;
}
}
|