001: // Copyright (c) 2001, 2002, 2006 Per M.A. Bothner.
002: // This is free software; for terms and warranty disclaimer see ./COPYING.
003:
004: package gnu.kawa.functions;
005:
006: import gnu.mapping.*;
007: import gnu.lists.*;
008: import gnu.math.RatNum;
009: import gnu.math.IntNum;
010: import java.io.PrintWriter;
011: import gnu.text.Char;
012: import gnu.kawa.xml.XmlNamespace;
013: import gnu.text.Printable; /* #ifdef use:java.util.regex */
014: import java.util.regex.*;
015:
016: /* #endif */
017:
018: /** Handle formatted output for Lisp-like languages. */
019:
020: public class DisplayFormat extends AbstractFormat {
021: /** Fluid parameter to specify default output base for printing rationals. */
022: public static final ThreadLocation outBase = new ThreadLocation(
023: "out-base");
024: static {
025: outBase.setGlobal(IntNum.ten());
026: }
027: /** True if we should print a radix indicator when printing rationals.
028: * The default is no; otherwise we follow Common Lisp conventions. */
029: public static final ThreadLocation outRadix = new ThreadLocation(
030: "out-radix");
031:
032: /** Create a new instance.
033: * @param readable if output should be formatted so it could be read
034: * back in again, for example strings shoudl be quoted.
035: * @param language the programming language style to use, where
036: * 'S' is Scheme, 'C' is Common Lisp, and 'E' is Emacs Lisp.
037: */
038: public DisplayFormat(boolean readable, char language) {
039: this .readable = readable;
040: this .language = language;
041: }
042:
043: public static DisplayFormat getEmacsLispFormat(boolean readable) {
044: return new DisplayFormat(readable, 'E');
045: }
046:
047: public static DisplayFormat getCommonLispFormat(boolean readable) {
048: return new DisplayFormat(readable, 'C');
049: }
050:
051: public static DisplayFormat getSchemeFormat(boolean readable) {
052: return new DisplayFormat(readable, 'S');
053: }
054:
055: boolean readable;
056:
057: /** 'S' is Scheme-style; 'C' is CommonLisp-style; 'E' is Emacs-style.
058: * Note Emacs has its own sub-class gnu.jemacs.lang.Print. */
059: char language;
060:
061: public boolean getReadableOutput() {
062: return readable;
063: }
064:
065: public void writeBoolean(boolean v, Consumer out) {
066: write(language == 'S' ? (v ? "#t" : "#f") : (v ? "t" : "nil"),
067: out);
068: }
069:
070: public void write(int v, Consumer out) {
071: if (!getReadableOutput())
072: Char.print(v, out);
073: else {
074: if (language == 'E' && v > ' ') {
075: out.write('?');
076: Char.print(v, out);
077: }
078: // else if (language == 'E') ...
079: else
080: write(Char.toScmReadableString(v), out);
081: }
082: }
083:
084: public void writeList(LList value, OutPort out) {
085: Object list = value;
086: out.startLogicalBlock("(", false, ")");
087: while (list instanceof Pair) {
088: if (list != value)
089: out.writeSpaceFill();
090: Pair pair = (Pair) list;
091: writeObject(pair.car, (Consumer) out);
092: list = pair.cdr;
093: }
094: if (list != LList.Empty) {
095: out.writeSpaceFill();
096: out.write(". ");
097: writeObject(LList.checkNonList(list), (Consumer) out);
098: }
099: out.endLogicalBlock(")");
100: }
101:
102: public void writeObject(Object obj, Consumer out) {
103: boolean space = false;
104: if (out instanceof OutPort
105: && !(obj instanceof gnu.kawa.xml.UntypedAtomic)
106: && !(obj instanceof Values)
107: && (getReadableOutput() || !(obj instanceof FString
108: || obj instanceof Char || obj instanceof Character))) {
109: ((OutPort) out).writeWordStart();
110: space = true;
111: }
112: writeObjectRaw(obj, out);
113: if (space)
114: ((OutPort) out).writeWordEnd();
115: }
116:
117: public void writeObjectRaw(Object obj, Consumer out) {
118: if (obj instanceof Boolean)
119: writeBoolean(((Boolean) obj).booleanValue(), out);
120: else if (obj instanceof Char)
121: write(((Char) obj).intValue(), out);
122: else if (obj instanceof Character)
123: write(((Character) obj).charValue(), out);
124: else if (obj instanceof Symbol) {
125: Symbol sym = (Symbol) obj;
126: if (sym.getNamespace() == XmlNamespace.HTML) {
127: write("html:", out);
128: write(sym.getLocalPart(), out);
129: } else
130: writeObject(obj.toString(), out);
131: }
132: /* #ifdef use:java.net.URI */
133: /* #ifdef use:java.lang.CharSequence */
134: else if (obj instanceof java.net.URI && getReadableOutput()
135: && out instanceof PrintWriter) {
136: write("#,(URI ", out);
137: Strings.printQuoted(obj.toString(), (PrintWriter) out, 1);
138: out.write(')');
139: }
140: /* #endif */
141: /* #endif */
142: else if (obj instanceof CharSeq) {
143: CharSeq str = (CharSeq) obj;
144: if (getReadableOutput() && out instanceof PrintWriter)
145: Strings.printQuoted(str, (PrintWriter) out, 1);
146: else if (obj instanceof FString) // FIXME Do we need this case?
147: {
148: FString fstr = (FString) obj;
149: out.write(fstr.data, 0, fstr.size());
150: } else
151: str.consume(0, str.size(), out);
152: } else if (obj instanceof LList && out instanceof OutPort)
153: writeList((LList) obj, (OutPort) out);
154: else if (obj instanceof SimpleVector) {
155: SimpleVector vec = (SimpleVector) obj;
156: String tag = vec.getTag();
157: String start, end;
158: if (language == 'E') {
159: start = "[";
160: end = "]";
161: } else {
162: start = tag == null ? "#(" : ("#" + tag + "(");
163: end = ")";
164: }
165: if (out instanceof OutPort)
166: ((OutPort) out).startLogicalBlock(start, false, end);
167: else
168: write(start, out);
169: int endpos = vec.size() << 1;
170: for (int ipos = 0; ipos < endpos; ipos += 2) {
171: if (ipos > 0 && out instanceof OutPort)
172: ((OutPort) out).writeSpaceFill();
173: if (!vec.consumeNext(ipos, out))
174: break;
175: }
176: if (out instanceof OutPort)
177: ((OutPort) out).endLogicalBlock(end);
178: else
179: write(end, out);
180: } else if (obj instanceof Array) {
181: write((Array) obj, 0, 0, out);
182: } else if (obj instanceof Consumable)
183: ((Consumable) obj).consume(out);
184: else if (obj instanceof Printable)
185: ((Printable) obj).print(out);
186: else if (obj instanceof RatNum) {
187: int b = 10;
188: boolean showRadix = false;
189: Object base = outBase.get(null);
190: Object printRadix = outRadix.get(null);
191: if (printRadix != null
192: && (printRadix == Boolean.TRUE || "yes"
193: .equals(printRadix.toString())))
194: showRadix = true;
195: if (base instanceof Number)
196: b = ((IntNum) base).intValue();
197: else if (base != null)
198: b = Integer.parseInt(base.toString());
199: String asString = ((RatNum) obj).toString(b);
200: if (showRadix) {
201: if (b == 16)
202: write("#x", out);
203: else if (b == 8)
204: write("#o", out);
205: else if (b == 2)
206: write("#b", out);
207: else if (b != 10 || !(obj instanceof IntNum))
208: write("#" + base + "r", out);
209: }
210: write(asString, out);
211: if (showRadix && b == 10 && obj instanceof IntNum)
212: write(".", out);
213: } else {
214: String asString;
215: if (obj == null)
216: asString = null;
217: else {
218: Class cl = obj.getClass();
219: if (cl.isArray()) {
220: int len = java.lang.reflect.Array.getLength(obj);
221: if (out instanceof OutPort)
222: ((OutPort) out).startLogicalBlock("[", false,
223: "]");
224: else
225: write("[", out);
226: for (int i = 0; i < len; i++) {
227: if (i > 0) {
228: write(" ", out);
229: if (out instanceof OutPort)
230: ((OutPort) out).writeBreakFill();
231: }
232: writeObject(
233: java.lang.reflect.Array.get(obj, i),
234: out);
235: }
236: if (out instanceof OutPort)
237: ((OutPort) out).endLogicalBlock("]");
238: else
239: write("]", out);
240: return;
241: }
242: asString = obj.toString();
243: }
244: if (asString == null)
245: write("#!null", out);
246: else if (readable && obj instanceof String)
247: writeReadableSymbol(asString, out);
248: else
249: write(asString, out);
250: }
251: }
252:
253: /** Recursive helper method for writing out Array (sub-) objects.
254: * @param array the Array to write out (part of).
255: * @param index the row-major index to start
256: * @param level the recurssion level, from 0 to array.rank()-1.
257: * @param out the destination
258: */
259: int write(Array array, int index, int level, Consumer out) {
260: int rank = array.rank();
261: int count = 0;
262: String start = level > 0 ? "(" : rank == 1 ? "#(" : "#" + rank
263: + "a(";
264: if (out instanceof OutPort)
265: ((OutPort) out).startLogicalBlock(start, false, ")");
266: else
267: write(start, out);
268: if (rank > 0) {
269: int size = array.getSize(level);
270: level++;
271: for (int i = 0; i < size; i++) {
272: if (i > 0) {
273: write(" ", out);
274: if (out instanceof OutPort)
275: ((OutPort) out).writeBreakFill();
276: }
277: int step;
278: if (level == rank) {
279: writeObject(array.getRowMajor(index), out);
280: step = 1;
281: } else
282: step = write(array, index, level, out);
283: index += step;
284: count += step;
285: }
286: }
287: if (out instanceof OutPort)
288: ((OutPort) out).endLogicalBlock(")");
289: else
290: write(")", out);
291: return count;
292: }
293:
294: /* #ifdef use:java.util.regex */
295: static Pattern r5rsIdentifierMinusInteriorColons = Pattern
296: .compile("(([a-zA-Z]|[!$%&*/:<=>?^_~])"
297: + "([a-zA-Z]|[!$%&*/<=>?^_~]|[0-9]|([-+.@]))*[:]?)"
298: + "|([-+]|[.][.][.])");
299:
300: /* #endif */
301:
302: void writeReadableSymbol(String sym, Consumer out) {
303: /* #ifdef use:java.util.regex */
304: /* Use |...| if symbol doesn't follow R5RS conventions
305: for identifiers or has a colon in the interior. */
306: if (!r5rsIdentifierMinusInteriorColons.matcher(sym).matches()) {
307: int len = sym.length();
308: if (len == 0) {
309: write("||", out);
310: } else {
311: boolean inVerticalBars = false;
312: for (int i = 0; i < len; i++) {
313: char ch = sym.charAt(i);
314: if (ch == '|') {
315: write(inVerticalBars ? "|\\" : "\\", out);
316: inVerticalBars = false;
317: } else if (!inVerticalBars) {
318: out.write('|');
319: inVerticalBars = true;
320: }
321: out.write(ch);
322: }
323: if (inVerticalBars)
324: out.write('|');
325: }
326: return;
327: }
328: /* #endif */
329: write(sym, out);
330: }
331: }
|