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.
5 package edu.berkeley.sbp.tib;
6 //import org.ibex.util.*;
7 //import org.ibex.io.*;
11 // TODO: multiple {{ }} for superquotation
16 * A slow, ugly, inefficient, inelegant, ad-hoc parser for TIB files.
18 * Despite being inelegant, this parser keeps all internal state on
19 * the stack (the only heap objects created are those which are
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
28 public class Tib /*implements Token.Stream<CharToken>*/ {
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); }
36 boolean right = false;
40 public Token next() throws IOException {
41 if (left) { left = false; return new CharToken(-3); }
42 if (right) { right = false; return new CharToken(-4); }
44 if (pos >= cur.size()) {
47 return new CharToken(-4);
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();
55 return new CharToken(-3);
58 public static Block parse(BufferedReader br) throws Invalid {
61 boolean blankLine = false;
62 Block top = new Block.Root();
63 for(String s = br.readLine(); s != null; s = br.readLine()) {
65 while (s.length() > 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;
73 top = top.closeIndent();
75 if (s.startsWith("{}")) {
77 boolean append = top instanceof Block.Literal && ((Block.Literal)top).braceCol == bc;
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);
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); }
88 for(int i=0; i<s.length(); i++) {
91 case '{': top = new Block.Brace(top, row, col); break;
92 case '}': top = top.closeBrace(); break;
100 while (ret.parent != null) ret = ret.parent;
102 } catch (InternalException ie) {
103 throw new Invalid(ie, row, col);
107 public static class Block implements Token {
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 = "";
115 public Tree<String> result() {
119 public Location getLocation() { return new Location.Cartesian(row, col); }
120 public boolean isEOF() { return false; }
122 public int size() { return children.size(); }
123 public Object child(int i) { return children.elementAt(i); }
124 public boolean isLiteral() { return false; }
126 protected Block(int row, int col) {
132 public Block(Block parent, int row, int col) {
135 this.iip = parent.size();
136 (this.parent = parent).add(this);
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;
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; }
156 public void finishWord() { if (pending.length() > 0) { add(pending); pending = ""; } }
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();
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));
177 if (justificationLimit==-1) {
179 ret.append(((Block)o).toString(indent+2, justificationLimit));
182 ret.append(((Block)o).toString(indent+2, justificationLimit));
185 line.append(o.toString());
188 ret.append(justify(line.toString(), indent, justificationLimit));
189 return ret.toString();
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); }
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(); }
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(); }
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));
227 ret.append(s.substring(0, nl));
228 s = s.substring(Math.min(s.length(),nl+1));
231 return ret.toString();
237 // Exceptions //////////////////////////////////////////////////////////////////////////////
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);
246 // Testing //////////////////////////////////////////////////////////////////////////////
248 public static void main(String[] s) throws Exception { System.out.println(parse(new Stream(System.in)).toString(-1)); }
250 // Utilities //////////////////////////////////////////////////////////////////////////////
252 public static String spaces(int i) { if (i<=0) return ""; return " " + spaces(i-1); }
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));
269 return ret.toString();