1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package no.sesat;
21
22 import java.io.BufferedReader;
23 import java.io.BufferedWriter;
24 import java.io.Console;
25 import java.io.FileReader;
26 import java.io.FileWriter;
27 import java.io.IOException;
28 import java.io.Reader;
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.Enumeration;
32 import java.util.HashMap;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.Map;
36 import java.util.Properties;
37 import java.util.Set;
38 import java.util.TreeSet;
39 import java.util.Vector;
40
41 import org.apache.log4j.Level;
42 import org.apache.log4j.Logger;
43 import org.apache.log4j.spi.LoggerRepository;
44
45
46
47
48
49
50 public class Interpreter {
51 public static final boolean ENABLED = Boolean.parseBoolean(System.getenv("SESAT_INTERPRETER"));
52 private static Map<String, Function> FUNCTIONS = ENABLED ? new HashMap<String, Function>() : null;
53 private static boolean RUN = ENABLED;
54 private static ArrayList<InterpreterRepl> REPL = new ArrayList<InterpreterRepl>();
55
56 private static interface FunctionInterface {
57 String execute(Context ctx);
58 }
59
60 private static abstract class InterpreterRepl extends Thread {
61 public InterpreterRepl(String name) {
62 super(name);
63 init();
64 }
65
66 protected void init() {
67 };
68
69 protected void output(String s) {
70 };
71 }
72
73 private static void broadcastToRepl(String s) {
74 for (InterpreterRepl repl : REPL) {
75 repl.output(s);
76 }
77 }
78
79
80
81
82
83
84
85
86
87 public synchronized static void addFunction(final String name, final Function fun) {
88 if (FUNCTIONS != null) {
89 if (FUNCTIONS.containsKey(name)) {
90 broadcastToRepl("Redefined function: " + name + "\n");
91 } else {
92 broadcastToRepl("Added function: " + name + "\n");
93 }
94 FUNCTIONS.put(name, fun);
95 }
96 }
97
98
99
100
101
102
103
104 public synchronized static void removeFunction(final String name) {
105 if (FUNCTIONS != null) {
106 broadcastToRepl("Removed function: " + name + "\n");
107 FUNCTIONS.remove(name);
108 }
109 }
110
111
112
113
114
115 public abstract static class Function implements FunctionInterface {
116
117
118
119
120
121 protected String describe() {
122 return "";
123 }
124
125
126
127
128
129
130
131 protected String help() {
132 return describe();
133 }
134 }
135
136
137
138
139 public static final class Context {
140
141
142
143 final private String argString;
144 final private Vector<String> argVector = new Vector<String>();
145 final private HashMap<String, String> argHash = new HashMap<String, String>();
146
147 private enum State {
148 normal, quote, keywordNormal, keywordQuote
149 };
150
151 private Context(final String arguments) {
152 argString = arguments.trim();
153 if (!argString.isEmpty()) {
154 State state = State.normal;
155
156 String arg = "";
157 String value = "";
158 for (char c : (argString + " ").toCharArray()) {
159 switch (state) {
160 case normal:
161 if (c == ' ') {
162 argVector.add(arg);
163 arg = "";
164 } else if (c == '\'') {
165 state = State.quote;
166 } else if (c == '=') {
167 state = State.keywordNormal;
168 } else {
169 arg += c;
170 }
171 break;
172
173 case quote:
174 if (c == '\'') {
175 state = State.normal;
176 } else {
177 arg += c;
178 }
179 break;
180
181 case keywordNormal:
182 if (c == ' ') {
183 state = State.normal;
184 argHash.put(arg.toUpperCase(), value);
185 arg = "";
186 value = "";
187 } else if (c == '\'') {
188 state = State.keywordQuote;
189 } else {
190 value += c;
191 }
192 break;
193
194 case keywordQuote:
195 if (c == '\'') {
196 state = State.keywordNormal;
197 } else {
198 value += c;
199 }
200 break;
201
202 }
203 }
204 }
205 }
206
207
208
209
210 public int length() {
211 return argVector.size();
212 }
213
214
215
216
217
218
219 public String getKeywordArgument(final String key) {
220 return argHash.get(key.toUpperCase());
221 }
222
223
224
225
226
227 public Set<String> getKeywords() {
228 return argHash.keySet();
229 }
230
231
232
233
234
235 public String[] getArgumentArray() {
236 String[] res = new String[argVector.size()];
237 return argVector.toArray(res);
238 }
239
240
241
242
243
244
245 public String getArgument(final int index) {
246 if (index < argVector.size()) {
247 return argVector.get(index);
248 } else
249 return null;
250 }
251
252
253
254
255
256 public String getArgumentString() {
257 return argString;
258 }
259
260 public String toString() {
261 String res = "";
262 res += "Raw argument string:\n " + argString + "\n";
263 res += "Argument array: " + length() + "\n";
264 for (String arg : getArgumentArray()) {
265 res += " " + arg + "\n";
266 }
267 res += "Keywords:\n";
268 for (String key : getKeywords()) {
269 res += " " + key + " --> " + getKeywordArgument(key) + "\n";
270 }
271 return res;
272 }
273 }
274
275 static {
276 if (ENABLED){
277
278
279 if(!System.getProperty("java.version").startsWith("1.5.")) {
280
281 final InterpreterRepl consoleReplThread = new InterpreterRepl("CONSOLE_REPL") {
282 Console console = System.console();
283
284 @Override
285 public void run() {
286 while (console != null) {
287 Reader r = console.reader();
288 char c = ' ';
289 try {
290 c = (char) r.read();
291 } catch (IOException e) {
292 return;
293 }
294
295 String line = "";
296 while (RUN && c != '\n') {
297 line += c;
298 try {
299 c = (char) r.read();
300 } catch (IOException e) {
301 return;
302 }
303 }
304 if (line != null) {
305
306 if (line.length() > 0) {
307 console.printf("%s\n", eval(line));
308 }
309 }
310 }
311 }
312
313 @Override
314 protected void output(String s) {
315 if (console != null) {
316 console.printf("%s\n", s);
317 }
318 }
319 };
320 consoleReplThread.setDaemon(true);
321 consoleReplThread.start();
322 REPL.add(consoleReplThread);
323 }
324
325 addFunction("echo", new Function() {
326 public String execute(final Context ctx) {
327 return ctx.getArgumentString();
328 }
329
330 public String describe() {
331 return "Echo arguments.";
332 }
333 });
334
335 addFunction("test-arguments", new Function() {
336 public String execute(final Context ctx) {
337 return ctx.toString();
338 }
339
340 public String describe() {
341 return "Just for testing.";
342 }
343 });
344
345 addFunction("help", new Function() {
346 public String execute(final Context ctx) {
347 if (ctx.length() == 1) {
348 String res = "Help for " + ctx.getArgument(0) + "\n";
349 final Function fun = FUNCTIONS.get(ctx.getArgument(0).trim());
350 if (fun != null) {
351 res += " " + fun.help();
352 } else {
353 res += " Not found!\n";
354 }
355 return res;
356 } else {
357 String res = "Functions available: \n";
358
359 List<String> tmp = new Vector<String>(FUNCTIONS.keySet());
360 Collections.sort(tmp);
361
362 for (String name : tmp) {
363 res += " " + name + "\n";
364 res += " " + FUNCTIONS.get(name).describe() + "\n";
365 }
366 return res;
367 }
368 }
369
370 public String describe() {
371 return "Print this help message";
372 }
373 });
374
375 addFunction("gc", new Function() {
376 public String execute(final Context ctx) {
377 System.gc();
378 return "GC requested";
379 }
380
381 public String describe() {
382 return "Request a gc run.";
383 }
384 });
385
386 addFunction("all-stacktraces", new Function() {
387 public String execute(final Context ctx) {
388 String res = "";
389 Map<Thread, StackTraceElement[]> m = Thread.getAllStackTraces();
390 for (Thread thread : m.keySet()) {
391 res += "Thread:: " + thread.toString() + "\n";
392 for (StackTraceElement ste : m.get(thread)) {
393 res += " " + ste.toString() + "\n";
394 }
395 res += "\n";
396 }
397 return res;
398 }
399
400 public String describe() {
401 return "Look at all stacktraces.";
402 }
403 });
404
405 addFunction("loggers", new Function() {
406 public String execute(final Context ctx) {
407 String res = "Active loggers:\n";
408 final LoggerRepository repo = Logger.getRootLogger().getLoggerRepository();
409
410 final Enumeration<?> e = repo.getCurrentLoggers();
411 while (e.hasMoreElements()) {
412 final Logger logger = (Logger) e.nextElement();
413 if (ctx.length() == 0 || (ctx.length() > 0 && logger.getName().matches(ctx.getArgument(0)))) {
414 res += logger.getName() + " " + logger.getLevel();
415 if (ctx.length() == 2) {
416 final Level level = Level.toLevel(ctx.getArgument(1));
417 if (level.toString().equalsIgnoreCase(ctx.getArgument(1))) {
418 res += " (Setting level to " + level + ")";
419 logger.setLevel(level);
420 } else {
421 res += " (unknown debug level: " + ctx.getArgument(1) + ")";
422 }
423 }
424 res += "\n";
425 }
426 }
427 return res;
428 }
429
430 public String describe() {
431 return "Print active loggers, and set level if specified. 'loggers [regexp] [level]'";
432 }
433 });
434
435 addFunction("properties", new Function() {
436 public String execute(final Context ctx) {
437 String res = "Properties: \n";
438 Properties p = System.getProperties();
439 TreeSet<Object> propKeys = new TreeSet<Object>(p.keySet());
440 for (Iterator<Object> it = propKeys.iterator(); it.hasNext();) {
441 String key = it.next().toString();
442 res += key + "=" + p.get(key) + "\n";
443 }
444 for (String key : ctx.getKeywords()) {
445 res += "Setting : " + key + " --> " + ctx.getKeywordArgument(key) + "\n";
446 System.setProperty(key, ctx.getKeywordArgument(key));
447 }
448 return res;
449 }
450
451 public String describe() {
452 return "List System.getProperties()";
453 }
454 });
455
456 addFunction("save", new Function() {
457 public String execute(final Context ctx) {
458 if (ctx.length() == 2) {
459 try {
460 String file = ctx.getArgument(1);
461 FileWriter s = new FileWriter(file);
462 BufferedWriter o = new BufferedWriter(s);
463 o.write(eval(ctx.getArgument(0)));
464 o.close();
465 return "Wrote output to file: " + file;
466 } catch (Exception e) {
467 return "Error: " + e.getMessage();
468 }
469 } else {
470 return "Argument count must be 2";
471 }
472 }
473
474 public String describe() {
475 return "Save output of function to file. 'save function filename'";
476 }
477 });
478
479 addFunction("read", new Function() {
480 public String execute(final Context ctx) {
481 if (ctx.length() == 1) {
482 try {
483 String file = ctx.getArgument(0);
484 FileReader s = new FileReader(file);
485 BufferedReader i = new BufferedReader(s);
486
487 String inline = "";
488 while ((inline = i.readLine()) != null) {
489 eval(inline);
490 }
491 i.close();
492 return "Read file: " + file;
493 } catch (Exception e) {
494 return "Error: " + e.getMessage();
495 }
496 } else {
497 return "Argument count must be 1";
498 }
499 }
500
501 public String describe() {
502 return "Read the file, and evaluate each line. 'read file'";
503 }
504 });
505
506 addFunction("macro", new Function() {
507 public String execute(final Context ctx) {
508 addFunction(ctx.getArgument(0), new Function() {
509 public String execute(final Context c) {
510 return eval(ctx.getArgumentString().substring(ctx.getArgument(0).length()).trim());
511 }
512 });
513 return ctx.getArgumentString();
514 }
515
516 public String describe() {
517 return "Make a macro";
518 }
519 });
520
521 addFunction("quit", new Function() {
522 public String execute(final Context ctx) {
523 RUN = false;
524 return "Bye!";
525 }
526
527 public String describe() {
528 return "Stop this interpreter.";
529 }
530 });
531 }
532 }
533
534
535
536
537
538
539
540
541 public synchronized static String eval(String expr) {
542 final int split = expr.trim().indexOf(" ");
543 final String function;
544 final String arguments;
545 if (split > 0) {
546 function = expr.substring(0, split);
547 arguments = expr.substring(split).trim();
548 } else {
549 function = expr;
550 arguments = "";
551 }
552 if (FUNCTIONS.containsKey(function)) {
553 try {
554 return FUNCTIONS.get(function).execute(new Context(arguments));
555 } catch (Throwable e) {
556 String error = "Error: " + e + "\n";
557 for (StackTraceElement ste : e.getStackTrace()) {
558 error += " " + ste.toString() + "\n";
559 }
560 return error + "\n";
561 }
562 } else {
563 return "Unknown function: " + function;
564 }
565 }
566 }