/
LispReader.java
153 lines (131 loc) · 3.98 KB
/
LispReader.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package de.npecomplete.mc.testproject.lisp.util;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.stream.IntStream;
import de.npecomplete.mc.testproject.lisp.LispException;
import de.npecomplete.mc.testproject.lisp.data.ArraySequence;
import de.npecomplete.mc.testproject.lisp.data.Keyword;
import de.npecomplete.mc.testproject.lisp.data.Symbol;
public class LispReader {
public static Object readStr(String s) {
return read(s.chars());
}
public static Object read(Reader reader) throws LispException {
return read(IntStream.generate(() -> {
try {
return reader.read();
} catch (IOException e) {
throw new LispException("Failed to read from reader", e);
}
}));
}
public static Iterator<Object> readMany(Reader reader) {
return readMany(IntStream.generate(() -> {
try {
return reader.read();
} catch (IOException e) {
throw new LispException("Failed to read from reader", e);
}
}));
}
public static Object read(IntStream chars) throws LispException {
LispTokenizer tokenizer = new LispTokenizer(chars.iterator());
return tokenizer.hasNext()
? build(tokenizer, null)
: null;
}
public static Iterator<Object> readMany(IntStream chars) {
LispTokenizer tokenizer = new LispTokenizer(chars.iterator());
return new Iterator<Object>() {
@Override
public boolean hasNext() {
return tokenizer.hasNext();
}
@Override
public Object next() {
return build(tokenizer, null);
}
};
}
private static Object build(Iterator<Token> it, Token expectedEnd) {
if (!it.hasNext()) {
throw new LispException("Encountered end of data while reading");
}
Token token = it.next();
switch (token.type) {
case SEQUENCE_END:
case LIST_END:
case MAP_SET_END:
if (token == expectedEnd) {
return token;
}
throw new LispException("Unexpected token while reading: "
+ token.type + " -> " + token.value);
case SEQUENCE_START:
ArrayList<Object> seqContent = new ArrayList<>();
buildCollection(seqContent, Token.SEQUENCE_END, it);
return new ArraySequence(seqContent.toArray(), 0);
case LIST_START:
ArrayList<Object> list = new ArrayList<>();
buildCollection(list, Token.LIST_END, it);
list.trimToSize();
return list;
case SET_START:
HashSet<Object> set = new HashSet<>();
buildCollection(set, Token.MAP_SET_END, it);
return set;
case MAP_START:
return buildMap(it);
case NULL:
case STRING:
case BOOLEAN:
case NUMBER:
return token.value;
case SYMBOL:
return new Symbol((String) token.value);
case KEYWORD:
return new Keyword((String) token.value);
case TAG:
// NOT YET SUPPORTED
}
throw new LispException("Token type " + token.type.name()
+ " not yet supported. Value was: " + token.value);
}
private static void buildCollection(Collection<Object> base, Token end, Iterator<Token> it) {
while (it.hasNext()) {
Object value = build(it, end);
if (value == end) {
return;
}
if (!base.add(value) && base instanceof Set) {
throw new LispException("Duplicate key in set literal: " + value);
}
}
throw new LispException("Encountered end of data while reading a collection");
}
private static Object buildMap(Iterator<Token> it) {
ArrayList<Object> mapContents = new ArrayList<>();
buildCollection(mapContents, Token.MAP_SET_END, it);
if (mapContents.size() % 2 != 0) {
throw new LispException("Odd number or elements for map literal");
}
Map<Object, Object> map = new HashMap<>(mapContents.size());
Iterator mapIt = mapContents.iterator();
while (mapIt.hasNext()) {
Object key = mapIt.next();
if (map.containsKey(key)) {
throw new LispException("Duplicate key in map literal: " + key);
}
map.put(key, mapIt.next());
}
return Collections.unmodifiableMap(map);
}
}