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