311fd29322d250285566a33b4579b7ac3f1319bb
[sbp.git] / src / edu / berkeley / sbp / GSS.java
1 package edu.berkeley.sbp;
2 import edu.berkeley.sbp.*;
3 import edu.berkeley.sbp.util.*;
4 import java.io.*;
5 import java.util.*;
6 import java.lang.reflect.*;
7
8 /** implements Tomita's Graph Structured Stack */
9 class GSS {
10
11     public GSS() { }
12
13     private Phase.Node[] reducing_list = null;
14
15     /** corresponds to a positions <i>between tokens</i> the input stream; same as Tomita's U_i's */
16     public class Phase implements Invokable<Parser.Table.State, Forest, GSS.Phase.Node> {
17
18         /** the token immediately after this phase */
19         public  final Token token;
20
21         boolean reducing = false;
22
23         /** currently this is necessary only for the code() hack -- it doesn't actually correspond to the input */
24         private final int pos;
25
26         /** FIXME */
27         public  Forest.Ref finalResult = null;
28
29         /** all nodes, keyed by the value returned by code() */
30         /*private*/ HashMap<Long,Phase.Node> hash    = new HashMap<Long,Phase.Node>();  /* ALLOC */
31
32         /** the number of nodes in this phase */
33         private int numNodes = 0;
34
35         boolean closed = false;
36
37         private Token.Location location;
38         public Phase(Phase previous, Token token, Token.Location location) {
39             this.pos = previous==null ? 0 : previous.pos+1;
40             this.token = token;
41             this.location = location;
42         }
43
44         public boolean isDone() { return token == null; }
45
46         private String error = "generic syntax error";
47         public void checkFailure() throws Parser.Failed {
48             if (token==null && finalResult==null)
49                 throw new Parser.Failed(error, getLocation());
50             if (numNodes <= 0)
51                 throw new Parser.Failed(error, getLocation());
52         }
53
54         public Token.Location getLocation() { return location; }
55
56         /** add a new node (merging with existing nodes if possible)
57          *  @param parent             the parent of the new node
58          *  @param result             the SPPF result corresponding to the new node
59          *  @param state              the state that the new node is in
60          *  @param fromEmptyReduction true iff this node is being created as a result of a reduction of length zero (see GRMLR paper)
61          *  @param start              the earliest part of the input contributing to this node (used to make merging decisions)
62          */
63         public void newNode(Node parent, Forest pending, Parser.Table.State state, boolean fromEmptyReduction) {
64             Node p = hash.get(code(state, parent==null?null:parent.phase()));
65             if (p != null)  newNode2(p, parent, pending, state, fromEmptyReduction);
66             else            newNode3(parent, pending, state, fromEmptyReduction);
67         }
68         private void newNode2(Node p, Node parent, Forest pending, Parser.Table.State state, boolean fromEmptyReduction) {
69             p.holder.merge(pending);
70             if (p.parents().contains(parent)) return;
71             //if (p.fe && p.phase() != parent.phase()) throw new Error("yep yep");
72             //if (!p.fe && p.phase() == parent.phase()) throw new Error("yep yep2");
73             p.parents().add(parent, true);
74             if (p!=parent && !fromEmptyReduction) p.queueReductions(parent);
75         }
76         private void newNode3(Node parent, Forest pending, Parser.Table.State state, boolean fromEmptyReduction) {
77             do {
78                 if (token != null && state.canShift(token)) break;
79                 if (state.isAccepting()) break;
80                 if (token==null) break;
81                 if (!state.canReduce(token)) return;
82                 //if (count > 1) break;
83                 //if (r.numPop == 0) break;
84                 //r.reduce(pending, parent, null, Phase.this, null);
85                 //return;
86             } while(false);
87
88             Node n = new Node(parent, pending, state, fromEmptyReduction);  // ALLOC
89             n.queueEmptyReductions();
90             if (!fromEmptyReduction) n.queueReductions(parent);
91         }
92
93         
94         /** perform all reduction operations */
95         public void reduce() {
96             reducing = true;
97             if (reducing_list==null || reducing_list.length < hash.size())
98                 reducing_list = new Phase.Node[hash.size() * 4];
99             Collection<Node> hv = hash.values();
100             hv.toArray(reducing_list);
101             int num = hv.size();
102             for(int i=0; i<num; i++) {
103                 Node n = reducing_list[i];
104                 n.queueEmptyReductions();
105                 // INVARIANT: we never "see" a node until its parent-set is complete, modulo merges
106             }
107             for(int i=0; i<num; i++) {
108                 Node n = reducing_list[i];
109                 reducing_list[i] = null;
110                 n.queueReductions();
111             }
112         }
113
114         public void invoke(Parser.Table.State st, Forest result, Node n) {
115             next.newNode(n, result, st, false);
116         }
117         private Phase next = null;
118
119         /** perform all shift operations, adding promoted nodes to <tt>next</tt> */
120         public void shift(Phase next, Forest result) {
121             this.next = next;
122             closed = true;
123             Forest res = null;
124             boolean ok = false;
125             for(Phase.Node n : hash.values()) {
126                 if (n.holder==null) continue;
127                 n.holder.resolve();
128                 if (token == null && n.state.isAccepting()) {
129                     ok = true;
130                     if (finalResult==null) finalResult = new Forest.Ref();
131                     finalResult.merge(n.holder);
132                 }
133                 if (!n.holder.valid()) continue;
134                 if (token == null) continue;
135                 n.state.invokeShifts(token, this, result, n);
136                 /*
137                 for(Parser.Table.State st : n.state.getShifts(token)) {
138                     if (res == null) res = result;
139                     next.newNode(n, res, st, true, this);
140                     ok = true;
141                 }
142                 */
143             }
144
145             if (!ok && token != null) {
146                 StringBuffer error = new StringBuffer();
147                 error.append("error: unable to shift token \"" + token + "\"\n");
148                 //error.append("  before: " +pendingReductions+ "\n");
149                 //error.append("  before: " +totalReductions+ "\n");
150                 //for(Phase.Node n : hash.values()) {
151                 //n.queueReductions();
152                 //n.queueEmptyReductions();
153                 //}
154                 //error.append("  after: " +pendingReductions+ "\n");
155                 //error.append("  candidate states:\n");
156                 //for(Phase.Node n : hash.values()) {
157                     //for(Sequence.Position p : n.state) error.append("        " + p + "\n");
158                     //error.append("        --\n");
159                 //for(Parser.Table.Reduction r : n.state.getReductions(token)) error.append("        " + r + "\n");
160                     //error.append("        ==\n");
161                 //}
162                 next.error = error.toString();
163             }
164
165             // this massively improves GC performance
166             hash = null;
167         }
168
169        
170         // GSS Nodes //////////////////////////////////////////////////////////////////////////////
171
172         /** a node in the GSS */
173         public final class Node extends FastSet<Node> implements Invokable<Parser.Table.Reduction, Node, Node> {
174
175             private Forest.Ref holder = null;
176             private boolean allqueued = false;
177
178             /** what state this node is in */
179             public final Parser.Table.State state;
180
181             /** which Phase this Node belongs to (node that Node is also a non-static inner class of Phase) */
182             public Phase phase() { return Phase.this; }
183
184             public  Forest.Ref holder() { return holder==null ? (holder = new Forest.Ref()) : holder; }
185             public  Forest pending() { return Phase.this.closed ? holder().resolve() : holder; }
186             public  FastSet<Node> parents() { return this; }
187
188             public void queueReductions() {
189                 if (!reducing) return;
190                 if (allqueued) return;
191                 allqueued = true;
192                 int where = parents().size();
193                 state.invokeReductions(token, this, this, null);
194             }
195
196             public void queueReductions(Node n2) {
197                 if (!allqueued) { queueReductions(); return; }
198                 state.invokeReductions(token, this, this, n2);
199             }
200
201             public final void invoke(Parser.Table.Reduction r, Node n, Node n2) {
202                 if (n==null) {
203                     if (r.numPop==0) r.reduce(this);
204                     return;
205                 }
206                 if (r.numPop==0) return;
207                 if (n2==null) r.reduce(n);
208                 else          r.reduce(n, n2);
209             }
210             public void queueEmptyReductions() {
211                 if (!reducing) return;
212                 state.invokeReductions(token, this, null, null);
213             }
214
215             private boolean fe;
216             private Node(Node parent, Forest pending, Parser.Table.State state, boolean fe) {
217                 this.fe = fe;
218                 this.state = state;
219                 Phase start = parent==null ? null : parent.phase();
220                 if (pending != null) this.holder().merge(pending);
221                 if (parent != null) parents().add(parent, true);
222                 if (Phase.this.hash.get(code(state, start)) != null) throw new Error("severe problem!");
223                 Phase.this.hash.put(code(state, start), this);
224                 Phase.this.numNodes++;
225                 if (parent==null) holder().valid = true; // hack to make sure that the "base" node is always considered valid
226             }
227         }
228
229     }
230
231     /** helper method */
232     private static boolean equal(Object a, Object b) {
233         if (a==null && b==null) return true;
234         if (a==null || b==null) return false;
235         return a.equals(b);
236     }
237
238     /** this is something of a hack right now */
239     private static long code(Parser.Table.State state, Phase start) {
240         return (((long)state.idx) << 32) | (start==null ? 0 : (start.pos+1));
241     }
242 }