1 package acl2s.uilib.cmd;
2
3 import java.io.Serializable;
4 import java.nio.ByteBuffer;
5 import java.nio.CharBuffer;
6 import java.nio.charset.Charset;
7 import java.nio.charset.CharsetDecoder;
8 import java.nio.charset.CharacterCodingException;
9 import java.util.ArrayList;
10
11 import org.peterd.util.Misc;
12
13 import acl2s.lib.parse.EmptyParse;
14 import acl2s.lib.parse.IParseContext;
15 import acl2s.lib.parse.ParseException;
16 import acl2s.lib.parse.Parser;
17 import acl2s.lib.parse.PositionableParser;
18 import acl2s.lib.parse.StringParser;
19 import acl2s.lib.parse.obj.Atom;
20 import acl2s.lib.parse.obj.Char;
21 import acl2s.lib.parse.obj.Cons;
22 import acl2s.lib.parse.obj.LispObject;
23 import acl2s.lib.parse.obj.NotListException;
24 import acl2s.lib.parse.obj.Num;
25 import acl2s.lib.parse.obj.ReadTimeConditional;
26 import acl2s.lib.parse.obj.Str;
27 import acl2s.lib.parse.obj.Sym;
28 import acl2s.lib.session.SessionException;
29 import acl2s.lib.session.SessionMode;
30 import acl2s.plugin.ISessionDocument;
31 import acl2s.plugin.prefs.InteractionPrefs;
32 import acl2s.uilib.session.InteractiveSessionManager;
33
34 public abstract class ResolvableUserInput implements Serializable {
35 private static final long serialVersionUID = 3L;
36
37 public abstract ResolvedInput resolve(InteractiveSessionManager session) throws SessionException;
38
39
40 protected final String text; // typed input (ish)
41
42 protected final String source;
43
44 public ResolvableUserInput(String text, String source) {
45 this.text = text;
46 this.source = source;
47 }
48
49 public String getInputString() {
50 return text;
51 }
52
53 public String getSource() {
54 return source;
55 }
56
57 public boolean equals(Object o) {
58 if (o instanceof ResolvableUserInput) {
59 ResolvableUserInput that = (ResolvableUserInput) o;
60 return this.exactlyEquals(that);
61 } else {
62 return false;
63 }
64 }
65
66 public boolean exactlyEquals(ResolvableUserInput that) {
67 return (this.text).equals(that.text);
68 }
69
70 public boolean abstractlyEquals(ResolvableUserInput that, IParseContext pc) {
71 LispObject[] thisLst = LispObject.robustParseAll(new StringParser(pc,this.text));
72 LispObject[] thatLst = LispObject.robustParseAll(new StringParser(pc,that.text));
73 if (thisLst.length != thatLst.length) return false;
74 for (int i = 0; i < thisLst.length; i++) {
75 if (!thisLst[i].equals(thatLst[i])) return false;
76 }
77 return true;
78 }
79
80 private static boolean isASCII(String text) {
81 // Test text for non-ASCII characters
82 byte [] bytearray = text.getBytes();
83 CharsetDecoder d = Charset.forName("US-ASCII").newDecoder();
84 try {
85 CharBuffer r = d.decode(ByteBuffer.wrap(bytearray));
86 r.toString();
87 } catch (CharacterCodingException cce) {
88 return false;
89 }
90 return true;
91 }
92
93
94 // CREATION/PARSING
95
96 public static ResolvableUserInput parse(PositionableParser p, String source, ISessionDocument sesdoc) throws ParseException, BadCmdException {
97 p.skipSpaceAndComments();
98 if (p.peek() == Parser.EOF) throw new EmptyParse();
99 int start = p.getPos();
100
101 IParseContext pc = p.getContext();
102 LispObject cmd = LispObject.parse(p); // default: entire expression
103 Boolean checkASCII = InteractionPrefs.getCheckASCII(null, sesdoc);
104 SessionMode mode = (checkASCII == null && sesdoc != null) ? sesdoc.getAcl2Mode() : null;
105 boolean mcheck = (mode == null) ? true : mode.getIntroductory();
106 if ((checkASCII == null ? mcheck :
107 checkASCII.booleanValue()) &&
108 !isASCII(cmd.toString(pc))) {
109 throw new BadCmdException("Input may not contain non-ASCII characters.");
110 }
111
112 LispObject cond = null; // default: unguarded
113 if (cmd instanceof ReadTimeConditional) {
114 ReadTimeConditional rtc = (ReadTimeConditional) cmd;
115 cond = rtc.fexpr;
116 cmd = rtc.oexpr;
117 }
118
119 String text = p.substring(start,p.getPos());
120
121 if (cmd instanceof Sym) {
122 Sym s = (Sym) cmd;
123 if (s.isKeyword()) { // keyword command
124 if (cond != null) {
125 throw new BadCmdException("Keyword commands preceded by read-time conditionals not supported.");
126 }
127 ArrayList<LispObject> params = new ArrayList<LispObject>();
128 for (;;) {
129 p.skip(true,false,true);
130 char c = p.peek();
131 if (c == '\n' || c == '\r' || c == Parser.EOF) break;
132 params.add(LispObject.parseOne(p));
133 }
134 if (p.peek() == Parser.EOF) {
135 throw new ParseException("Keyword command must be terminated by a newline outside of a lisp expression.");
136 }
137 text = p.substring(start,p.getPos());
138 p.skip();
139 return new UnresolvedKeywordCmd(text,s,params.toArray(new LispObject[params.size()]),source);
140 }
141 }
142
143 if (((cmd instanceof Cons) || (cmd instanceof Num)) && text.charAt(text.length()-1) == ')') {
144 // Cons or Complex ending in ) is fine
145 } else {
146 // need whitespace; Char ending in ) is NOT fine
147 if ("\n\r\t\f ;".indexOf(p.peek()) < 0) {
148 throw new ParseException("This form must be followed by whitespace, so that modifying text after it cannot change how it would be parsed.");
149 }
150 p.skip();
151 }
152
153 if (cmd instanceof Cons) {
154 LispObject[] objs;
155 try {
156 objs = ((Cons) cmd).listToArray();
157 if (objs == null || objs.length < 1) throw new NotListException();
158 } catch (NotListException e) {
159 throw new BadCmdException("Invocation must be a true list.");
160 }
161 if (!(objs[0] instanceof Sym)) {
162 // old
163 //throw new BadCmdException("First element of invocation list must be a symbol.");
164 }
165
166 } else if (cmd instanceof Atom && cond == null) {
167 Atom a = (Atom) cmd;
168 if (a instanceof Char || a instanceof Num) {
169 return new SimpleValueInput(text, a, source);
170 } else if (a instanceof Str) {
171 Str s = (Str) a;
|
Event do_not_call: |
"java.lang.String.toUpperCase()" implicitly uses the environment's default character set, which might lead to unexpected behavior. Consider using toUpperCase(Locale locale). |
172 if (!Misc.equal(s.value.toUpperCase(),s.value)) { // can't be a package name
173 return new SimpleValueInput(text, a, source);
174 }
175 // strings with no lowercase letters could be VALUE or COMMAND
176 }
177 // syms could be BAD or VALUE
178 }
179
180 return new UnresolvedCmd(text,cmd,cond,source);
181 }
182
183
184 }