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  	}