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