9a546e9d149978da05c19ae6eadbc574b9b1864b
[sbp.git] / src / edu / berkeley / sbp / tib / Tib.java
1 // Copyright 2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
4
5 package edu.berkeley.sbp.tib;
6 //import org.ibex.util.*;
7 //import org.ibex.io.*;
8 import java.util.*;
9 import java.io.*;
10
11 // TODO: multiple {{ }} for superquotation
12 // TODO: strings
13 // TODO: comments
14
15 /**
16  *   A slow, ugly, inefficient, inelegant, ad-hoc parser for TIB files.
17  *
18  *   Despite being inelegant, this parser keeps all internal state on
19  *   the stack (the only heap objects created are those which are
20  *   returned).
21  *
22  *   This was written as an ad-hoc parser to facilitate
23  *   experimentation with the TIB spec.  Once the spec is finalized it
24  *   should probably be rewritten using a parser-generator, if
25  *   possible (it is unclear whether or not the associated grammar is
26  *   context-free).
27  */
28 public class Tib /*implements Token.Stream<CharToken>*/ {
29     /*
30     public Tib(String s) { this(new StringReader(s)); }
31     public Tib(Reader r) { this(new BufferedReader(r)); }
32     public Tib(InputStream is) { this(new BufferedReader(new InputStreamReader(is))); }
33     public Tib(BufferedReader br) { cur = parse(br).toString(0,-1); }
34
35     boolean left = false;
36     boolean right = false;
37     private Block cur;
38     int pos = 0;
39
40     public Token next() throws IOException {
41         if (left) { left = false; return new CharToken(-3); }
42         if (right) { right = false; return new CharToken(-4); }
43
44         if (pos >= cur.size()) {
45             pos = cur.iip+1;
46             cur = cur.parent;
47             return new CharToken(-4);
48         }
49
50         Object o = cur.child(pos++);
51         if (o instanceof String) return new StringToken((String)o);
52         if (o.isLiteral()) return ((Block.Literal)o).text();
53         cur = (Block)b;
54         pos = 0;
55         return new CharToken(-3);
56     }
57
58     public static Block parse(BufferedReader br) throws Invalid {
59         int row=0, col=0;
60         try {
61             boolean blankLine = false;
62             Block top = new Block.Root();
63             for(String s = br.readLine(); s != null; s = br.readLine()) {
64                 col = 0;
65                 while (s.length() > 0 &&
66                        s.charAt(0) == ' ' &&
67                        (!(top instanceof Block.Literal) || col < top.col)) { col++; s = s.substring(1); }
68                 if ((top instanceof Block.Literal) && col >= top.col) { top.add(s); continue; }
69                 if (s.length()==0)                                    { blankLine = true; continue; }
70                 while (col < top.col) {
71                     if (s.startsWith("{}") && top instanceof Block.Literal && ((Block.Literal)top).braceCol == col) break;
72                     blankLine = false;
73                     top = top.closeIndent();
74                 }
75                 if (s.startsWith("{}")) {
76                     int bc = col;
77                     boolean append = top instanceof Block.Literal && ((Block.Literal)top).braceCol == bc;
78                     s = s.substring(2);
79                     col += 2;
80                     while (s.length() > 0 && s.charAt(0) == ' ' && !(append && col >= top.col) ) { col++; s = s.substring(1); }
81                     if (append) top.add(s); else (top = new Block.Literal(top, row, col, bc)).add(s);
82                     continue;
83                 }
84                 while (s.length() > 0 && s.charAt(s.length()-1)==' ') { s = s.substring(0, s.length()-1); }
85                 if (col > top.col) top = new Block.Indent(top, row, col);
86                 else if (blankLine) { top = top.closeIndent(); top = new Block.Indent(top, row, col); }
87                 blankLine = false;
88                 for(int i=0; i<s.length(); i++) {
89                     top.add(s.charAt(i));
90                     switch(s.charAt(i)) {
91                         case '{':  top = new Block.Brace(top, row, col);   break;
92                         case '}':  top = top.closeBrace();                 break;
93                     }
94                 }
95                 top.add(' ');
96                 top.finishWord();
97             }
98             // FIXME
99             Block ret = top;
100             while (ret.parent != null) ret = ret.parent;
101             return ret;
102         } catch (InternalException ie) {
103             throw new Invalid(ie, row, col);
104         }
105     }
106
107     public static class Block implements Token {
108                       Block  parent;
109         public  final int    row;
110         public  final int    col;
111         public final int iip;
112         private final Vec    children = new Vec();
113         private       String pending  = "";
114
115         public Tree<String> result() {
116             // FIXME
117         }
118
119         public Location getLocation() { return new Location.Cartesian(row, col); }
120         public boolean isEOF() { return false; }
121
122         public int    size() { return children.size(); }
123         public Object child(int i) { return children.elementAt(i); }
124         public boolean isLiteral() {  return false; }
125
126         protected Block(int row, int col)                {
127             this.row=row;
128             this.col=col;
129             this.iip = -1;
130             parent = null;
131         }
132         public    Block(Block parent, int row, int col)  {
133             this.row=row;
134             this.col=col;
135             this.iip = parent.size();
136             (this.parent = parent).add(this);
137         }
138
139         public void    add(String s)        { children.addElement(s); }
140         public void    add(char c)          {
141             if (c == '{' || c == '}') { finishWord(); return; }
142             if (c != ' ') { pending += c; return; }
143             if (pending.length() > 0) { finishWord(); add(" "); return; }
144             if (size()==0) return;
145             if (child(size()-1).equals(" ")) return;
146             add(" ");
147             return;
148         }
149
150         public void    add(Block  b)        { children.addElement(b); }
151         public Block   promote()            { parent.parent.replaceLast(this); return close(); }
152         public Object  lastChild()          { return children.lastElement(); }
153         public Block   lastChildAsBlock()   { return (Block)lastChild(); }
154         public void    replaceLast(Block b) { children.setElementAt(b, children.size()-1); b.parent = this; }
155
156         public void    finishWord()         { if (pending.length() > 0) { add(pending); pending = ""; } }
157
158         public Block   closeBrace()         { throw new InternalException("attempt to closeBrace() a "+getClass().getName()); }
159         public Block   closeIndent()        { throw new InternalException("attempt to closeIndent() a "+getClass().getName()); }
160         public Block   close() {
161             while(size() > 0 && child(size()-1).equals(" ")) children.setSize(children.size()-1);
162             if (size()==0) throw new InternalException("PARSER BUG: attempt to close an empty block (should never happen)");
163             if (size() > 1 || !(lastChild() instanceof Block)) return parent;
164             return lastChildAsBlock().promote();
165         }
166         public String toString() { return toString(80); }
167         public String toString(int justificationLimit) { return toString(0, 80); }
168         protected String toString(int indent, int justificationLimit) {
169             StringBuffer ret = new StringBuffer();
170             StringBuffer line = new StringBuffer();
171             for(int i=0; i<children.size(); i++) {
172                 Object o = children.elementAt(i);
173                 if (i>0 && children.elementAt(i-1) instanceof Block && justificationLimit!=-1) ret.append("\n");
174                 if (o instanceof Block) {
175                     ret.append(justify(line.toString(), indent, justificationLimit));
176                     line.setLength(0);
177                     if (justificationLimit==-1) {
178                         ret.append("{");
179                         ret.append(((Block)o).toString(indent+2, justificationLimit));
180                         ret.append("}");
181                     } else {
182                         ret.append(((Block)o).toString(indent+2, justificationLimit));
183                     }
184                 } else {
185                     line.append(o.toString());
186                 }
187             }
188             ret.append(justify(line.toString(), indent, justificationLimit));
189             return ret.toString();
190         }
191
192         private static class Root extends Block {
193             public Root() { super(0, Integer.MIN_VALUE); }
194             public Block close() { throw new InternalException("attempted to close top block"); }
195             public String toString(int justificationLimit) { return toString(-2, justificationLimit); }
196         }
197
198         private static class Brace  extends Block {
199             public Brace(Block parent, int row, int col) { super(parent, row, col); }
200             public Block closeBrace() { return super.close(); }
201         }
202     
203         private static class Indent extends Block {
204             public Indent(Block parent, int row, int col) { super(parent, row, col); }
205             public Block closeIndent() { return super.close(); }
206         }
207
208         private static class Literal extends Block {
209             private StringBuffer content = new StringBuffer();
210             public final int braceCol;
211             public    Literal(Block parent, int row, int col, int braceCol) { super(parent,row,col); this.braceCol = braceCol; }
212             public    boolean isLiteral() {  return true; }
213             public    int size() { return 1; }
214             public    Object child(int i) { return i==0 ? content.toString() : null; }
215             public    Block  close() { return parent; }
216             public    Block  closeIndent() { return close(); }
217             public    void   add(String s) { if (content.length()>0) content.append('\n'); content.append(s); }
218             public    String text() { return content.toString(); }
219             protected String toString(int indent, int justificationLimit) {
220                 StringBuffer ret = new StringBuffer();
221                 String s = content.toString();
222                 while(s.length() > 0) {
223                     int nl = s.indexOf('\n');
224                     if (nl==-1) nl = s.length();
225                     ret.append(spaces(indent));
226                     ret.append("{}  ");
227                     ret.append(s.substring(0, nl));
228                     s = s.substring(Math.min(s.length(),nl+1));
229                     ret.append('\n');
230                 }
231                 return ret.toString();
232             }
233         }
234     }
235
236
237     // Exceptions //////////////////////////////////////////////////////////////////////////////
238
239     private static class InternalException extends RuntimeException { public InternalException(String s) { super(s); } }
240     public static class Invalid extends IOException {
241         public Invalid(InternalException ie, int row, int col) {
242             super(ie.getMessage() + " at " + row + ":" + col);
243         }
244     }
245
246     // Testing //////////////////////////////////////////////////////////////////////////////
247
248     public static void main(String[] s) throws Exception { System.out.println(parse(new Stream(System.in)).toString(-1)); }
249     
250     // Utilities //////////////////////////////////////////////////////////////////////////////
251
252     public static String spaces(int i) { if (i<=0) return ""; return " " + spaces(i-1); }
253
254     private static String justify(String s, int indent, int justificationLimit) {
255         if (s.length() == 0) return "";
256         if (justificationLimit==-1) return s;
257         StringBuffer ret = new StringBuffer();
258         while(s.length() > 0) {
259             if (s.charAt(0) == ' ') { s = s.substring(1); continue; }
260             ret.append(spaces(indent));
261             int i = s.indexOf(' ');
262             if (i==-1) i = s.length();
263             while(s.indexOf(' ', i+1) != -1 && s.indexOf(' ', i+1) < justificationLimit-indent) i = s.indexOf(' ', i+1);
264             if (s.length() + indent < justificationLimit) i = s.length();
265             ret.append(s.substring(0, i));
266             s = s.substring(i);
267             ret.append('\n');
268         }
269         return ret.toString();
270     }
271     */
272 }
273