5c1b92f41f83ff066c676ae4be667a2630d444f5
[sbp.git] / src / edu / berkeley / sbp / GSS.java
1 // Copyright 2006 all rights reserved; see LICENSE file for BSD-style license
2
3 package edu.berkeley.sbp;
4 import edu.berkeley.sbp.*;
5 import edu.berkeley.sbp.util.*;
6 import edu.berkeley.sbp.Parser.Table.*;
7 import edu.berkeley.sbp.Sequence.Position;
8 import java.io.*;
9 import java.util.*;
10 import java.lang.reflect.*;
11
12 /** implements Tomita's Graph Structured Stack */
13 class GSS {
14
15     Input input;
16     private Parser parser;
17     public GSS(Input input, Parser parser) { this.input = input; this.parser = parser;}
18     public Input getInput() { return input; }
19
20     int numNewNodes = 0;
21     int numOldNodes = 0;
22     int viewPos = 0;
23     int numReductions = 0;
24
25     /** corresponds to a positions <i>between tokens</i> the input stream; same as Tomita's U_i's */
26     class Phase<Tok> implements Invokable<State, Result>, IntegerMappable, GraphViz.ToGraphViz, Iterable<Node> {
27
28         // FIXME: right now, these are the performance bottleneck
29         private HashMapBag<Integer,Sequence>       performed       = new HashMapBag<Integer,Sequence>();
30
31         public Forest.Many finalResult;
32         private PriorityQueue<Reduction> reductionQueue = new PriorityQueue<Reduction>();
33
34         Parser parser() { return parser; }
35         public void addReduction(Reduction r) {
36             //System.out.println("+ " + r);
37             parser.spin();
38             reductionQueue.add(r);
39         }
40         public void invoke(State st, Result result) {
41             parser.spin();
42             good |= next.newNode(result, st, false);
43         }
44
45         /** the token immediately after this phase */
46         final Tok token;
47         final int pos;
48
49         public IntPairMap<Node> hash = new IntPairMap<Node>();  /* ALLOC */
50         private boolean good = false;
51         private Phase next = null;
52         private Phase prev;
53         private Input.Location location;
54         private Input.Location nextLocation;
55         private Input.Location prevLocation;
56         
57         private Forest forest;
58
59         public Phase(State startState) throws ParseFailed, IOException {
60             this(null, null);
61             Result primordealResult = new Result(null, null, null);
62             newNode(primordealResult, startState, true);
63         }
64         public Phase(Phase prev, Forest forest) throws ParseFailed, IOException {
65             this.prevLocation = input.getLocation();
66             this.token = (Tok)input.next();
67             this.location = input.getLocation();
68             this.prev = prev;
69             this.forest = forest;
70             this.pos = prev==null ? 0 : prev.pos+1;
71             this.nextLocation = input.getLocation();
72             if (prev != null) prev.shift(this, forest);
73             numReductions = 0;
74
75             int minPhasePos = Integer.MAX_VALUE;
76             int maxOrd = -1;
77             Reduction best = null;
78             //System.out.println("==============================================================================");
79             while(!reductionQueue.isEmpty()) {
80                 Reduction r = reductionQueue.poll();
81                 //System.out.println("- " + r);
82                 if (r.parentPhase() != null)
83                     if (r.parentPhase().pos > minPhasePos)
84                         throw new Error();
85                 r.perform();
86                 if (r.parentPhase() != null) {
87                     if (r.parentPhase().pos < minPhasePos) {
88                         minPhasePos = r.parentPhase().pos;
89                         maxOrd = r.reduction().ord;
90                         best = r;
91                     } else if (r.parentPhase().pos == minPhasePos) {
92                         /*
93                         if (best != null && Parser.mastercache.comparePositions(r.reduction(), best.reduction()) < 0)
94                             throw new Error("\n"+r+"\n"+best+"\n"+
95                                             Parser.mastercache.comparePositions(r.reduction(), best.reduction())+"\n"+r.compareTo(best)+
96                                             "\n"+(r.reduction().ord-best.reduction().ord));
97                         */
98                         maxOrd = r.reduction().ord;
99                         best = r;
100                     }
101                 }
102                 numReductions++;
103             }
104             if (token==null) shift(null, null);
105         }
106
107         public boolean isDone() throws ParseFailed {
108             if (token != null) return false;
109             if (token==null && finalResult==null)
110                 ParseFailed.error("unexpected end of file", this, null,
111                                   getLocation().createRegion(getLocation()));
112             return true;
113         }
114
115         public Input.Location getPrevLocation() { return prevLocation; }
116         public Input.Location getLocation() { return location; }
117         public Input.Region getRegion() { return prevLocation.createRegion(location); }
118         public Input.Location getNextLocation() { return nextLocation; }
119         public boolean        isFrontier() { return hash!=null; }
120
121         /** perform all shift operations, adding promoted nodes to <tt>next</tt> */
122         private void shift(Phase next, Forest result) throws ParseFailed {
123             this.next = next;
124             // this massively improves GC performance
125             if (prev != null) {
126                 IntPairMap<Node> h = prev.hash;
127                 prev.hash = null;
128                 prev.performed = null;
129                 for(Node n : h)
130                     n.check();
131             }
132             numOldNodes = hash.size();
133             for(Node n : hash.values()) {
134                 if (token == null && n.state().isAccepting()) {
135                     if (finalResult==null) finalResult = new Forest.Many();
136                     for(Result r : n)
137                         finalResult.merge(r.getForest());
138                 }
139                 if (token == null) continue;
140                 n.state().invokeShifts(token, this, new Result(result, n, null));
141             }
142             numNewNodes = next==null ? 0 : next.hash.size();
143             viewPos = this.pos;
144             if (!good && token!=null) {
145                 String toks = token+"";
146                 if (toks.length()==1 && toks.charAt(0) == edu.berkeley.sbp.chr.CharAtom.left) {
147                     ParseFailed.error("unexpected increase in indentation", this,
148                                       token, getRegionFromThisToNext());
149                 } else if (toks.length()==1 && toks.charAt(0) == edu.berkeley.sbp.chr.CharAtom.right) {
150                     ParseFailed.error("unexpected decrease in indentation", this,
151                                       token, getRegionFromThisToNext());
152                 } else {
153                     ParseFailed.error("unexpected character '"+ANSI.cyan(StringUtil.escapify(token+"",
154                                                                                              "\\\'\r\n"))+"'",
155                                       this, token, getRegionFromThisToNext());
156                 }
157             }
158             if (token==null && finalResult==null)
159                 ParseFailed.error("unexpected end of file", this, null,
160                                   getLocation().createRegion(getLocation()));
161             for(Node n : hash) n.check();
162         }
163
164         void newNodeFromReduction(Result result, State state, Position reduction) {
165             int pos = result.phase().pos;
166             Sequence owner = reduction.owner();
167             for(Sequence s : owner.hates())
168                 if (performed.contains(pos, s))
169                     return;
170             for(Sequence s : owner.needs())
171                 if (!performed.contains(pos, s))
172                     return;
173             if (owner.needed_or_hated && !performed.contains(pos, owner))
174                 performed.add(pos, owner);
175             if (state!=null)
176                 newNode(result, state, reduction.pos<=0);
177         }
178
179         /** add a new node (merging with existing nodes if possible)
180          *  @param parent             the parent of the new node
181          *  @param result             the SPPF result corresponding to the new node
182          *  @param state              the state that the new node is in
183          *  @param fromEmptyReduction true iff this node is being created as a result of a reduction of length zero (see GRMLR paper)
184          *  @param start              the earliest part of the input contributing to this node (used to make merging decisions)
185          */
186         private boolean newNode(Result result, State state, boolean fromEmptyReduction) {
187             Node p = hash.get(state, result.phase());
188             if (p != null) { p.addResult(result); return true; }
189             do {
190                 if (token != null && state.canShift(token)) break;
191                 if (state.isAccepting()) break;
192                 if (token==null) break;
193                 if (!state.canReduce(token)) return false;
194             } while(false);
195             Node n = new Node(Phase.this, result, state, fromEmptyReduction);  // ALLOC
196             for(Object s : state.conjunctStates)
197                 newNode(new Result(null, n, null), (State)s, fromEmptyReduction);
198             return true;
199         }
200
201         public int toInt() { return pos+1; }
202         public int size() { return hash==null ? 0 : hash.size(); }
203         public int pos() { return pos; }
204         public Tok getToken() { return token; }
205         public Iterator<Node> iterator() { return hash.iterator(); }
206         public GSS getGSS() { return GSS.this; }
207
208         // GraphViz //////////////////////////////////////////////////////////////////////////////
209
210         public GraphViz.Node toGraphViz(GraphViz gv) {
211             if (gv.hasNode(this)) return gv.createNode(this);
212             GraphViz.Group g = gv.createGroup(this);
213             g.label = "Phase " + pos;
214             g.color = "gray";
215             g.cluster = true;
216             return g;
217         }
218         public boolean isTransparent() { return false; }
219         public boolean isHidden() { return false; }
220
221         public void dumpGraphViz(String filename) throws IOException {
222             FileOutputStream fos = new FileOutputStream(filename);
223             PrintWriter p = new PrintWriter(new OutputStreamWriter(fos));
224             GraphViz gv = new GraphViz();
225             for(Object n : this)
226                 ((Node)n).toGraphViz(gv);
227             gv.dump(p);
228             p.flush();
229             p.close();
230         }
231
232     }
233
234 }