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