/
ZList.java
194 lines (168 loc) · 4.95 KB
/
ZList.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
package de.lucaswerkmeister.graaleneyj.runtime;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.InvalidArrayIndexException;
import com.oracle.truffle.api.interop.TruffleObject;
import com.oracle.truffle.api.interop.UnknownIdentifierException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.library.ExportLibrary;
import com.oracle.truffle.api.library.ExportMessage;
import de.lucaswerkmeister.graaleneyj.ZConstants;
import de.lucaswerkmeister.graaleneyj.library.ZTypeIdentityLibrary;
/**
* An object that behaves like a list in other languages: the Z10 list
* constructor (with head and tail), or the Z13 nil value.
*
* I originally tried having this as an abstract class, with subclasses for the
* cons and nil case, but that crashed javac when the nil subclass had its own
* export annotations (for behaving like null in interop). Combining both into
* one class is how Mumbler did it, too.
*/
@ExportLibrary(ZTypeIdentityLibrary.class)
@ExportLibrary(InteropLibrary.class)
public final class ZList extends ZObject {
private final Object head;
private final ZList tail;
private final long length; // cached for efficiency
public static final ZList NIL = new ZList();
private ZList() {
super(STATIC_BLANK_SHAPE);
head = null;
tail = null;
length = 0;
}
public ZList(Object head, ZList tail) {
super(STATIC_BLANK_SHAPE);
assert head != null; // TODO throw instead of assert?
assert tail != null; // TODO throw instead of assert?
this.head = head;
this.tail = tail;
this.length = 1 + tail.length;
}
public Object getHead() {
assert this != NIL;
return head;
}
public ZList getTail() {
assert this != NIL;
return tail;
}
@ExportMessage
public String getTypeIdentity() {
return ZConstants.LIST;
}
@ExportMessage
public final boolean hasArrayElements() {
return true;
}
@ExportMessage
public final Object readArrayElement(long index) throws InvalidArrayIndexException {
if (!isArrayElementReadable(index)) {
throw InvalidArrayIndexException.create(index);
}
if (index == 0) {
return head;
} else {
return tail.readArrayElement(index - 1);
}
}
@ExportMessage
public final long getArraySize() {
return length;
}
@ExportMessage
public final boolean isArrayElementReadable(long index) {
return index >= 0 && index < getArraySize();
}
@ExportMessage
public final boolean hasMembers() {
return true;
}
@ExportMessage
public final ZListKeys getMembers(boolean includeInternal) {
return new ZListKeys();
}
@ExportMessage
public final boolean isMemberReadable(String member) {
return ZConstants.ZOBJECT_TYPE.equals(member) || ZConstants.LIST_HEAD.equals(member)
|| ZConstants.LIST_TAIL.equals(member);
}
@ExportMessage
public final Object readMember(String member) throws UnknownIdentifierException {
switch (member) {
case ZConstants.ZOBJECT_TYPE:
return new ZReference(ZConstants.LIST);
case ZConstants.LIST_HEAD:
if (this == NIL) {
return new ZReference(ZConstants.LISTISNIL);
} else {
return head;
}
case ZConstants.LIST_TAIL:
if (this == NIL) {
return new ZReference(ZConstants.LISTISNIL);
} else {
return tail;
}
}
throw UnknownIdentifierException.create(member);
}
@ExportMessage
@TruffleBoundary
public final String toDisplayString(boolean allowSideEffects, @CachedLibrary(limit = "0") InteropLibrary interops) {
if (this == NIL) {
return "[]";
} else if (tail == NIL) {
return "[" + interops.toDisplayString(head, allowSideEffects) + "]";
} else {
return "[" + interops.toDisplayString(head, allowSideEffects) + ", "
+ tail.toDisplayString(allowSideEffects, interops).substring(1);
}
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof ZList)) {
return false;
}
if (this == NIL || obj == NIL) {
return this == obj;
}
ZList list = (ZList) obj;
return length == list.length && head.equals(list.head) && tail.equals(list.tail);
}
/**
* Helper object for {@link ZList#getMembers()}. All lists have exactly the
* members Z1K1/type, Z10K1/head, and Z10K2/tail.
*/
@ExportLibrary(InteropLibrary.class)
static final class ZListKeys implements TruffleObject {
@ExportMessage
public boolean hasArrayElements() {
return true;
}
@ExportMessage
public boolean isArrayElementReadable(long index) {
return 0 <= index && index < 3;
}
@ExportMessage
public long getArraySize() {
return 3;
}
@ExportMessage
public String readArrayElement(long index) throws InvalidArrayIndexException {
if (0 <= index && index < 3) {
switch ((int) index) {
case 0:
return ZConstants.ZOBJECT_TYPE;
case 1:
return ZConstants.LIST_HEAD;
case 2:
return ZConstants.LIST_TAIL;
}
}
CompilerDirectives.transferToInterpreter();
throw InvalidArrayIndexException.create(index);
}
}
}