1 package acl2s.lib.parse.obj;
2
3 import java.util.HashMap;
4
5 import org.peterd.util.Misc;
6
7 import acl2s.lib.parse.IPackageDefinition;
8 import acl2s.lib.parse.IPackageWorld;
9 import acl2s.lib.parse.NoSuchPackageException;
10 import acl2s.lib.parse.ObjectNotSupported;
11 import acl2s.lib.parse.IParseContext;
12 import acl2s.lib.parse.ParseException;
13 import acl2s.lib.parse.Parser;
14 import acl2s.lib.parse.SymOrNumFns;
15 import acl2s.lib.session.BootstrapParseContext;
16
17 // interning allows us to compare sym and pack with ==
18 public class Sym extends SymOrNum {
19 private static final long serialVersionUID = 1L;
20
21 public final String pack;
22 public final String sym;
23
24 Sym(String pack, String s) {
25 this.pack = pack.intern();
26 this.sym = s.intern();
27 }
28
29 String computeString(IParseContext pc) {
30 String defPkg = pc == null ? null : pc.getDefaultPackage();
31 StringBuilder b = new StringBuilder();
32 if (pack.equals(keywordPkgName)) {
33 b.append(":");
34 } else {
35 boolean prependPackage = true;
36
37 try {
38 if (Misc.equal(defPkg,pack) ||
39 (pc != null && // kill warning
40 defPkg != null && // subsumes `pc != null`
41 Misc.equal(pc.internedPackage(defPkg,sym),pack))) {
42 prependPackage = false;
43 }
44 } catch (NoSuchPackageException e) {
45 // shouldn't happen that default package doesn't exist
46 }
47
48 if (prependPackage) {
49 b.append(partToString(pack));
50 b.append("::");
51 }
52 }
53 b.append(partToString(sym));
54 return b.toString();
55 }
56
57 public static String partToString(String s) {
58 int len = s.length();
59 if (len == 0) {
60 return barredToString(s);
61 }
62 if (len > 0) {
63 char c = s.charAt(0);
64 if (Character.isDigit(c)) {
65 return barredToString(s);
66 }
67 if (c == '-' && len > 1) {
68 return barredToString(s);
69 }
70 if (c == '_' || c == '^') {
71 return barredToString(s);
72 }
73 }
74 for (int i = 0; i < len; i++) {
75 if (!isSafe(s.charAt(i))) {
76 return barredToString(s);
77 }
78 }
79 return s;
80 }
81
82 static String barredToString(String s) {
83 StringBuilder b = new StringBuilder();
84 b.append('|');
85 for (int i = 0; i < s.length(); i++) {
86 char c = s.charAt(i);
87 if (c == '\\' || c == '|') {
88 b.append('\\');
89 }
90 b.append(c);
91 }
92 b.append('|');
93 return b.toString();
94 }
95
96 public boolean isNil() {
97 return this == nilInstance;
98 }
99
100 public boolean probablyNil() {
101 return sym == "NIL";
102 }
103
104 public boolean isKeyword() {
105 return pack == keywordPkgName;
106 }
107
108 public boolean isACL2ConstantName() {
109 return
110 this == nilInstance ||
111 this == tInstance ||
112 (sym.length() > 2 && sym.startsWith("*") && sym.endsWith("*"));
113 }
114
115 public boolean isACL2EventName() {
116 return !isKeyword() && !pack.equals(mainLispPkgName);
117 }
118
119 public boolean isACL2VariableName() {
120 return !isKeyword() && !isACL2ConstantName() && !isReservedLLKeyword() &&
121 (pack != mainLispPkgName || BootstrapParseContext.acl2Pkg.internedPackage(sym) == mainLispPkgName);
122 }
123
124 public boolean isReservedLLKeyword() {
125 return sym.startsWith("&");
126 }
127
128 public boolean interned() { return true; }
129
130 public Sym resolve(IParseContext pc) throws ParseException {
131 return this;
132 }
133
134 public boolean equals(Object o) {
135 return this == o; // because of database
136 }
137
138 public int hashCode() {
|
Event overridden_event: |
The method "acl2s.lib.parse.obj.Sym.hashCode()" is not overriden in a subclass. |
| Also see events: |
[overriding_event] |
139 return sym.hashCode() + (pack != null ? pack.hashCode() * 3 : 42);
140 }
141
142 public int hashcode() {
143 return super.hashCode(); // because of database
144 }
145
146 public static Sym parse(Parser p) throws ParseException {
147 char c = p.peek();
148 if (c == '(') {
149 p.advance(1);
150 p.skipSpaceAndComments();
151 if (p.peek() != ')') {
152 throw new ParseException("Expected: ()");
153 }
154 p.advance(1);
155 return nilInstance;
156 }
157 if (c == '.') {
158 int i = 1;
159 while (p.peek(i) == '.') i++;
160 if (SymOrNumFns.isTerminator(p.peek(i))) {
161 p.advance(i);
162 throw new ParseException("Illegal dot(s).");
163 }
164 } else if (c == '#') {
165 p.advance(1);
166 p.skipToTerminator();
167 throw new ParseException("Unrecognized #-sequence");
168 }
169 if (SymOrNumFns.isTerminator(p.peek())) {
170 p.skip();
171 throw new ParseException("Symbol expected.");
172 }
173 StringBuilder s1 = new StringBuilder();
174 boolean mustBeExternal = false;
175 boolean firstCharColon = false;
176 c = p.peek();
177 if (c == ':' && p.peek(1) != ':') {
178 firstCharColon = true;
179 p.skip();
180 // p.skipSpace(); // GCL-ism. not portable
181 c = p.peek();
182 if (SymOrNumFns.isTerminator(c)) {
183 throw new ParseException("For portability, symbol syntax must immediately follow package delimiter.");
184 }
185 }
186 while (!SymOrNumFns.isTerminator(c) && (firstCharColon || c != ':')) {
187 if (c == '\\') {
188 c = p.next();
189 if (c == Parser.EOF) throw new ParseException("EOF after \\.");
190 s1.append(c);
191 } else if (c == '|') {
192 c = p.next();
193 while (c != '|' && c != Parser.EOF) {
194 if (c == '\\') {
195 c = p.next();
196 if (c == Parser.EOF) throw new ParseException("EOF after \\.");
197 s1.append(c);
198 } else if (c == '\n' || c == '\r') {
199 throw new ParseException("Unescaped newlines in symbols are not supported in ACL2s.");
200 } else {
201 s1.append(c);
202 }
203 c = p.next();
204 }
205 if (c == Parser.EOF) throw new ParseException("EOF before matching |.");
206 } else {
207 s1.append(Character.toUpperCase(c));
208 }
209 c = p.next();
210 }
211 if (firstCharColon) {
212 return create(p.getContext(),keywordPkgName,s1.toString());
213 } else if (c == ':') {
214 StringBuilder s2 = new StringBuilder();
215 p.skip();
216 if (p.peek() == ':') {
217 p.skip();
218 } else {
219 mustBeExternal = true;
220 }
221 // p.skipSpace(); // GCL-ism. not portable
222 c = p.peek();
223 if (SymOrNumFns.isTerminator(c)) {
224 throw new ParseException("For portability, symbol syntax must immediately follow package delimiter.");
225 }
226 do {
227 if (c == '\\') {
228 c = p.next();
229 if (c == Parser.EOF) throw new ParseException("EOF after \\.");
230 s2.append(c);
231 } else if (c == '|') {
232 c = p.next();
233 while (c != '|' && c != Parser.EOF) {
234 if (c == '\\') {
235 c = p.next();
236 if (c == Parser.EOF) throw new ParseException("EOF after \\.");
237 s2.append(c);
238 } else if (c == '\n' || c == '\r') {
239 throw new ParseException("Unescaped newlines in symbols are not supported in ACL2s.");
240 } else {
241 s2.append(c);
242 }
243 c = p.next();
244 }
245 if (c == Parser.EOF) throw new ParseException("EOF before matching |.");
246 } else if (c == ':') {
247 p.skipToTerminator();
248 throw new ParseException("Extra : after :: in symbol designator.");
249 } else {
250 s2.append(Character.toUpperCase(c));
251 }
252 c = p.next();
253 } while (!SymOrNumFns.isTerminator(c));
254 if (mustBeExternal) {
255 throw new ObjectNotSupported("External symbol references not supported in ACL2. Use '::'.");
256 }
257 return create(p.getContext(),s1.toString(),s2.toString());
258 } else {
259 return create(p.getContext(),s1.toString());
260 }
261 }
262
263 public static Sym createACL2(IParseContext pc, String sym) {
264 try {
265 return create(pc,acl2PkgName,sym);
266 } catch (ParseException e) {
267 e.printStackTrace(); // won't happen
268 return null;
269 }
270 }
271
272 public static Sym createKeyword(String sym) {
273 return SymDb.get(keywordPkgName, sym); // never any imports, etc.
274 }
275
276 public static Sym create(IParseContext pc, String sym) throws ParseException {
277 return create(pc,null,sym);
278 }
279
280 public static Sym create(IParseContext pc, String pack, String sym) throws ParseException {
281 if (pack == null) {
282 if (pc != null) {
283 pack = pc.getDefaultPackage();
284 }
285 if (pack == null) { // don't know what package it's supposed to go in
286 return new UnpackagedSym(sym);
287 }
288 }
289 String pkgNameError = checkPkgName(pack);
290 if (pkgNameError != null) {
291 throw new ObjectNotSupported(pkgNameError);
292 }
293 String truePack = null;
294 if (pc != null) {
295 truePack = pc.internedPackage(pack,sym); // could throw NoSuchPackageException
296 }
297 if (truePack == null) { // unknown how it should be interned
298 return new UninternedSym(pack,sym);
299 } else {
300 return SymDb.get(truePack,sym);
301 }
302 }
303
304 public static Sym create(IPackageWorld pwrld, String pack, String sym) throws NoSuchPackageException {
305 if (pack == null) {
306 return new UnpackagedSym(sym);
307 }
308 if (pwrld != null) {
309 IPackageDefinition pdef = pwrld.getPackageDefinitions().get(pack);
310 if (pdef == null) throw new NoSuchPackageException(pack);
311 return SymDb.get(pdef.internedPackage(sym),sym);
312 } else {
313 return new UninternedSym(pack,sym);
314 }
315 }
316
317 public static Sym bootstrapCreate(String string) {
318 try {
319 return create(BootstrapParseContext.instance,acl2PkgName,string);
320 } catch (ParseException e) {
321 // impossible;
322 return null;
323 }
324 }
325
326 // this is a little wacky in ACL2 because any non-empty string is allowed as a package name
327 public static Sym pkgWitness(String pkg) {
328 if (pkg == null || pkg.length() == 0) pkg = "ACL2";
329 return SymDb.get(pkg, witnessName);
330 }
331
332 public static String checkPkgName(String pkg) {
333 int len = pkg.length();
334 if (len == 0) {
335 return "The empty string is not a valid ACL2 package name.";
336 }
337 for (int i = 0; i < len; i++) {
338 if (Character.isLowerCase(pkg.charAt(i))) {
339 return "ACL2 package names may not contain lower-case characters.";
340 }
341 }
342 if (pkg.startsWith("ACL2_*1*_")) {
343 return "An ACL2 package name may not start with \"ACL2_*1*_\". These are used internally.";
344 }
345 return null; // all good
346 }
347
348 public static Sym bool(boolean b) {
349 return b ? tInstance : nilInstance;
350 }
351
352 // string constants automatically interned
353 public static final String acl2PkgName = "ACL2";
354 public static final String mainLispPkgName = "COMMON-LISP";
355 public static final String keywordPkgName = "KEYWORD";
356 public static final String witnessName = "ACL2-PKG-WITNESS";
357
358 static class SymDb {
359 static final HashMap<String,Sym> map = new HashMap<String,Sym>();
360 public synchronized static Sym get(String pack, String sym) {
361 Sym ret;
362 String key = pack + '\0' + sym;
363 ret = map.get(key);
364 if (ret == null) {
365 ret = new Sym(pack,sym);
366 map.put(key,ret);
367 }
368 return ret;
369 }
370 }
371
372 public static final Sym functionInstance = SymDb.get(mainLispPkgName,"FUNCTION");
373 public static final Sym appendInstance = SymDb.get(mainLispPkgName,"APPEND");
374 public static final Sym quoteInstance = SymDb.get(mainLispPkgName,"QUOTE");
375 public static final Sym consInstance = SymDb.get(mainLispPkgName,"CONS");
376 public static final Sym nilInstance = SymDb.get(mainLispPkgName,"NIL");
377 public static final Sym tInstance = SymDb.get(mainLispPkgName,"T");
378
379 public static final boolean isSafe(char c) {
380 return c < safeLookup.length && safeLookup[c];
381 }
382 static final String safe = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890*/{}[]<>?+=-_&^%$@!~";
383
384 static boolean[] safeLookup;
385 static {
386 int x = -1;
387 for (int i = 0; i < safe.length(); i++) {
388 if (safe.charAt(i) > x) {
389 x = safe.charAt(i);
390 }
391 }
392 safeLookup = new boolean[x + 1]; // initialized to falses
393 for (int i = 0; i < safe.length(); i++) {
394 safeLookup[safe.charAt(i)] = true;
395 }
396 }
397 }