summaryrefslogtreecommitdiff
path: root/Homework/cs5300/project-one/p1-scanner/scanner/Scanner.java
blob: 2581dcbac0c1f6758e282a46748e2e8b6ba90bcd (plain) (blame)
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;
  }
}