Source code: com/memoire/silk/SilkInputPort.java
1
2
3 package com.memoire.silk;
4 import com.memoire.silk.*;
5
6
7 import java.io.*;
8
9 /** SilkInputPort is to SilkScheme as InputStream is to Java.
10 * @author Peter Norvig, peter@norvig.com http://www.norvig.com
11 * Copyright 1998 Peter Norvig, see http://www.norvig.com/license.html **/
12
13 public class SilkInputPort extends SilkSchemeUtils {
14
15 static String EOF = "#!EOF";
16 boolean isPushedToken = false;
17 boolean isPushedChar = false;
18 Object pushedToken = null;
19 int pushedChar = -1;
20 Reader in;
21 StringBuffer buff = new StringBuffer();
22
23 /** Construct an SilkInputPort from an InputStream. **/
24 public SilkInputPort(InputStream in) { this.in = new InputStreamReader(in);}
25
26 /** Construct an SilkInputPort from a Reader. **/
27 public SilkInputPort(Reader in) { this.in = in;}
28
29 /** @GDX */
30 private int in_read() throws IOException
31 {
32 while(!in.ready())
33 {
34 if(exited_) return -1;
35 try { Thread.sleep(100); }
36 catch(InterruptedException ex) { }
37 }
38 return in.read();
39 }
40
41 boolean exited_=false;
42
43 /** Read and return a SilkScheme character or EOF. **/
44 public Object readChar() {
45 try {
46 if (isPushedChar) {
47 isPushedChar = false;
48 if (pushedChar == -1) return EOF; else return chr((char)pushedChar);
49 } else {
50 int ch = in_read();
51 if (ch == -1) return EOF; else return chr((char)ch);
52 }
53 } catch (IOException e) {
54 warn("On input, exception: " + e);
55 return EOF;
56 }
57 }
58
59 /** Peek at and return the next SilkScheme character (or EOF).
60 * However, don't consume the character. **/
61 public Object peekChar() {
62 int p = peekCh();
63 if (p == -1) return EOF; else return chr((char)p);
64 }
65
66 /** Push a character back to be re-used later. **/
67 int pushChar(int ch) {
68 isPushedChar = true;
69 return pushedChar = ch;
70 }
71
72 /** Pop off the previously pushed character. **/
73 int popChar() {
74 isPushedChar = false;
75 return pushedChar;
76 }
77
78 /** Peek at and return the next SilkScheme character as an int, -1 for EOF.
79 * However, don't consume the character. **/
80 public int peekCh() {
81 try { return isPushedChar ? pushedChar : pushChar(in_read()); }
82 catch (IOException e) {
83 warn("On input, exception: " + e);
84 return -1;
85 }
86 }
87
88 /** Read and return a SilkScheme expression, or EOF. **/
89 public Object read() {
90 try {
91 Object token = nextToken();
92 if (token == "(")
93 return readTail(false);
94 else if (token == ")")
95 { warn("Extra ) ignored."); return read(); }
96 else if (token == ".")
97 { warn("Extra . ignored."); return read(); }
98 else if (token == "'")
99 return list("quote", read());
100 else if (token == "`")
101 return list("quasiquote", read());
102 else if (token == ",")
103 return list("unquote", read());
104 else if (token == ",@")
105 return list("unquote-splicing", read());
106 else
107 return token;
108 } catch (IOException e) {
109 warn("On input, exception: " + e);
110 return EOF;
111 }
112 }
113
114 /** Close the port. Return TRUE if ok. **/
115 public Object close() {
116 try { this.in.close(); return TRUE; }
117 catch (IOException e) { return error("IOException: " + e); }
118 }
119
120 /** Is the argument the EOF object? **/
121 public static boolean isEOF(Object x) { return x == EOF; }
122
123 Object readTail(boolean dotOK) throws IOException {
124 Object token = nextToken();
125 if (token == EOF)
126 return error("EOF during read.");
127 else if (token == ")")
128 return null;
129 else if (token == ".") {
130 Object result = read();
131 token = nextToken();
132 if (token != ")") warn("Where's the ')'? Got " +
133 token + " after .");
134 return result;
135 } else {
136 isPushedToken = true;
137 pushedToken = token;
138 return cons(read(), readTail(true));
139 }
140 }
141
142 Object nextToken() throws IOException {
143 int ch;
144
145 // See if we should re-use a pushed char or token
146 if (isPushedToken) {
147 isPushedToken = false;
148 return pushedToken;
149 } else if (isPushedChar) {
150 ch = popChar();
151 } else {
152 ch = in_read();
153 }
154
155 // Skip whitespace
156 while (Character.isWhitespace((char)ch)) ch = in_read();
157
158 // See what kind of non-white character we got
159 switch(ch) {
160 case -1: return EOF;
161 case '(' : return "(";
162 case ')': return ")";
163 case '\'': return "'";
164 case '`': return "`";
165 case ',':
166 ch = in_read();
167 if (ch == '@') return ",@";
168 else { pushChar(ch); return ","; }
169 case ';':
170 // Comment: skip to end of line and then read next token
171 while(ch != -1 && ch != '\n' && ch != '\r') ch = in_read();
172 return nextToken();
173 case '"':
174 // Strings are represented as char[]
175 buff.setLength(0);
176 while ((ch = in_read()) != '"' && ch != -1) {
177 buff.append((char) ((ch == '\\') ? in_read() : ch));
178 }
179 if (ch == -1) warn("EOF inside of a string.");
180 return buff.toString().toCharArray();
181 case '#':
182 switch (ch = in_read()) {
183 case 't': case 'T': return TRUE;
184 case 'f': case 'F': return FALSE;
185 case '(':
186 pushChar('(');
187 return listToVector(read());
188 case '\\':
189 ch = in_read();
190 if (ch == 's' || ch == 'S' || ch == 'n' || ch == 'N') {
191 pushChar(ch);
192 Object token = nextToken();
193 if (token == "space") return chr(' ');
194 else if (token == "newline") return chr('\n');
195 else {
196 isPushedToken = true;
197 pushedToken = token;
198 return chr((char)ch);
199 }
200 } else {
201 return chr((char)ch);
202 }
203 case 'e': case 'i': case 'd': return nextToken();
204 case 'b': case 'o': case 'x':
205 warn("#" + ((char)ch) + " not implemented, ignored.");
206 return nextToken();
207 default:
208 warn("#" + ((char)ch) + " not recognized, ignored.");
209 return nextToken();
210 }
211 default:
212 buff.setLength(0);
213 int c = ch;
214 do {
215 buff.append((char)ch);
216 ch = in_read();
217 } while (!Character.isWhitespace((char)ch) && ch != -1 &&
218 ch != '(' && ch != ')' && ch != '\'' && ch != ';'
219 && ch != '"' && ch != ',' && ch != '`');
220 pushChar(ch);
221 // Try potential numbers, but catch any format errors.
222 if (c == '.' || c == '+' || c == '-' || (c >= '0' && c <= '9')) {
223 try { return new Double(buff.toString()); }
224 catch (NumberFormatException e) { ; }
225 }
226 return buff.toString().toLowerCase().intern();
227 }
228 }
229 }
230
231