1   // Copyright 2013 Bill Campbell, Swami Iyer and Bahar Akbal-Delibas
2   
3   package jminusminus;
4   
5   import java.io.FileNotFoundException;
6   import java.util.Stack;
7   import java.util.Vector;
8   
9   /**
10   * A lexical analyzer for j-- that interfaces with the hand-written parser
11   * (Parser.java). It provides a backtracking mechanism, and makes use of the
12   * underlying hand-written Scanner.
13   */
14  
15  class LookaheadScanner {
16  
17      /** The underlying hand-written scanner. */
18      private Scanner scanner;
19  
20      /** Backtracking queue. */
21      private Vector<TokenInfo> backtrackingQueue;
22  
23      /** Token queue. */
24      private Vector<TokenInfo> nextQueue;
25  
26      /** Stack of token queues for nested lookahead. */
27      private Stack<Vector<TokenInfo>> queueStack;
28  
29      /** Whether we are looking ahead. */
30      public boolean isLookingAhead;
31  
32      /** Previous token. */
33      private TokenInfo previousToken;
34  
35      /** Current token. */
36      private TokenInfo token;
37  
38      /**
39       * Construct a LookaheadScanner from a file name.
40       * 
41       * @param fileName
42       *            the name of the file containing the source.
43       * @exception FileNotFoundException
44       *                when the named file cannot be found.
45       */
46  
47      public LookaheadScanner(String fileName) throws FileNotFoundException {
48          scanner = new Scanner(fileName);
49          backtrackingQueue = new Vector<TokenInfo>();
50          nextQueue = new Vector<TokenInfo>();
51          queueStack = new Stack<Vector<TokenInfo>>();
52          isLookingAhead = false;
53      }
54  
55      /**
56       * Scan to the next token in the input.
57       */
58  
59      public void next() {
60          previousToken = token;
61          if (backtrackingQueue.size() == 0) {
62              token = scanner.getNextToken();
63          } else {
64              token = backtrackingQueue.remove(0);
65          }
66          if (isLookingAhead) {
67              nextQueue.add(token);
68          }
69      }
70  
71      /**
72       * Record the current position in the input, so that we can start looking
73       * ahead in the input (and later return to this position). We'll queue up
74       * the current and subsequent tokens until returnToPosition() is invoked.
75       * These recordPosition's can be nested.
76       */
77  
78      public void recordPosition() {
79          isLookingAhead = true;
80          queueStack.push(nextQueue);
81          nextQueue = new Vector<TokenInfo>();
82          nextQueue.add(previousToken);
83          nextQueue.add(token);
84      }
85  
86      /**
87       * Return to the previously recorded position in the input stream of tokens.
88       * If this is a nested lookahead, then return to the previous token queue.
89       */
90  
91      public void returnToPosition() {
92          while (backtrackingQueue.size() > 0) {
93              nextQueue.add(backtrackingQueue.remove(0));
94          }
95          backtrackingQueue = nextQueue;
96          nextQueue = queueStack.pop();
97          isLookingAhead = !(queueStack.empty());
98  
99          // Restore previous and current tokens
100         previousToken = backtrackingQueue.remove(0);
101         token = backtrackingQueue.remove(0);
102     }
103 
104     /**
105      * The currently scanned token.
106      * 
107      * @return the current token.
108      */
109 
110     public TokenInfo token() {
111         return token;
112     }
113 
114     /**
115      * The previously scanned token. We use this in the parser to get at a
116      * token's semantic info (for example an identifier's name), after we've
117      * scanned it.
118      * 
119      * @return the previous token.
120      */
121 
122     public TokenInfo previousToken() {
123         return previousToken;
124     }
125 
126     /**
127      * Has an error occurred up to now in lexical analysis?
128      * 
129      * @return true or false.
130      */
131 
132     public boolean errorHasOccured() {
133         return scanner.errorHasOccurred();
134     }
135 
136     /**
137      * Return the name of the source file.
138      * 
139      * @return name of the source file.
140      */
141 
142     public String fileName() {
143         return scanner.fileName();
144     }
145 
146 }
147