source: branches/1.0.x/abcl/src/org/armedbear/lisp/scripting/AbclScriptEngine.java

Last change on this file was 13101, checked in by astalla, 15 years ago

Reduced verbosity of the AbclScriptEngine?

  • Property svn:eol-style set to LF
File size: 10.8 KB
Line 
1/*
2 * AbclScriptEngine.java
3 *
4 * Copyright (C) 2008 Alessio Stalla
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21package org.armedbear.lisp.scripting;
22
23import java.io.File;
24import java.io.IOException;
25import java.io.InputStream;
26import java.io.Reader;
27import java.io.StringWriter;
28import java.util.Map;
29
30import javax.script.*;
31
32import org.armedbear.lisp.*;
33
34
35public class AbclScriptEngine extends AbstractScriptEngine implements Invocable, Compilable {
36
37    private Interpreter interpreter;
38    /**
39     * The function used to evaluate a string of code.
40     */
41    private Function evalScript;
42    /**
43     * The function used to evaluate a Lisp function.
44     */
45    private Function evalFunction;
46    /**
47     * The function used to compile Lisp code.
48     */
49    private Function compileScript;
50    /**
51     * The function used to evaluate a compiled script.
52     */
53    Function evalCompiledScript;
54
55    protected AbclScriptEngine() {
56  interpreter = Interpreter.getInstance();
57  if(interpreter == null) {
58      interpreter = Interpreter.createInstance();
59  }
60  loadFromClasspath("/org/armedbear/lisp/scripting/lisp/packages.lisp");
61  loadFromClasspath("/org/armedbear/lisp/scripting/lisp/abcl-script.lisp");
62  loadFromClasspath("/org/armedbear/lisp/scripting/lisp/config.lisp");
63  if(getClass().getResource("/abcl-script-config.lisp") != null) {
64            //TODO: find a way to log this if wanted
65            //System.out.println("ABCL: loading configuration from " + getClass().getResource("/abcl-script-config.lisp"));
66      loadFromClasspath("/abcl-script-config.lisp");
67  }
68  ((Function) interpreter.eval("#'abcl-script:configure-abcl")).execute(new JavaObject(this));
69        //System.out.println("ABCL: configured");
70  evalScript = (Function) this.findSymbol("EVAL-SCRIPT", "ABCL-SCRIPT").getSymbolFunction();
71  compileScript = (Function) this.findSymbol("COMPILE-SCRIPT", "ABCL-SCRIPT").getSymbolFunction();
72  evalCompiledScript = (Function) this.findSymbol("EVAL-COMPILED-SCRIPT", "ABCL-SCRIPT").getSymbolFunction();
73  evalFunction = (Function) this.findSymbol("EVAL-FUNCTION", "ABCL-SCRIPT").getSymbolFunction();
74    }
75   
76    public Interpreter getInterpreter() {
77  return interpreter;
78    }
79
80    public void setStandardInput(InputStream stream, LispThread thread) {
81  thread.setSpecialVariable(Symbol.STANDARD_INPUT, new Stream(Symbol.SYSTEM_STREAM, stream, Symbol.CHARACTER, true));
82    }
83   
84    public void setStandardInput(InputStream stream) {
85  setStandardInput(stream, LispThread.currentThread());
86    }
87   
88    public void setInterpreter(Interpreter interpreter) {
89  this.interpreter = interpreter;
90    }
91
92    public static String escape(String s) {
93  StringBuffer b = new StringBuffer();
94  int len = s.length();
95  char c;
96  for (int i = 0; i < len; ++i) {
97      c = s.charAt(i);
98      if (c == '\\' || c == '"') {
99    b.append('\\');
100      }
101      b.append(c);
102  }
103  return b.toString();
104    }
105
106  public LispObject loadFromClasspath(String classpathResource) {
107    InputStream istream = getClass().getResourceAsStream(classpathResource);
108    Stream stream = new Stream(Symbol.SYSTEM_STREAM, istream, Symbol.CHARACTER);
109    return load(stream);
110  }
111
112    public LispObject load(Stream stream) {
113        Symbol keyword_verbose = Lisp.internKeyword("VERBOSE");
114        Symbol keyword_print = Lisp.internKeyword("PRINT");
115        /*
116         * load (filespec &key (verbose *load-verbose*) (print *load-print*)
117         * (if-does-not-exist t) (external-format :default)
118         */
119        return Symbol.LOAD.getSymbolFunction().execute
120            (new LispObject[] { stream, keyword_verbose, Lisp.NIL,
121                                keyword_print, Lisp.NIL,
122                                Keyword.IF_DOES_NOT_EXIST, Lisp.T,
123                                Keyword.EXTERNAL_FORMAT, Keyword.DEFAULT });
124    }
125
126  public LispObject load(String filespec) {
127    return load(filespec, true);
128  }
129
130  public LispObject load(String filespec, boolean compileIfNecessary) {
131    if (isCompiled(filespec) || !compileIfNecessary) {
132      return interpreter.eval("(load \"" + escape(filespec) + "\")");
133    } else {
134      return compileAndLoad(filespec);
135    }
136  }
137
138  public static boolean isCompiled(String filespec) {
139    if (filespec.endsWith(".abcl")) {
140      return true;
141    }
142    File source;
143    File compiled;
144    if (filespec.endsWith(".lisp")) {
145      source = new File(filespec);
146      compiled = new File(filespec.substring(0, filespec.length() - 5)
147          + ".abcl");
148    } else {
149      source = new File(filespec + ".lisp");
150      compiled = new File(filespec + ".abcl");
151    }
152    if (!source.exists()) {
153      throw new IllegalArgumentException("The source file " + filespec + " cannot be found");
154    }
155    return compiled.exists()
156        && compiled.lastModified() >= source.lastModified();
157  }
158
159  public LispObject compileFile(String filespec) {
160    return interpreter.eval("(compile-file \"" + escape(filespec) + "\")");
161  }
162
163  public LispObject compileAndLoad(String filespec) {
164    return interpreter.eval("(load (compile-file \"" + escape(filespec) + "\"))");
165  }
166
167  public static boolean functionp(LispObject obj) {
168    return obj instanceof Function;
169  }
170
171  public JavaObject jsetq(String symbol, Object value) {
172    Symbol s = findSymbol(symbol);
173    JavaObject jo;
174    if (value instanceof JavaObject) {
175      jo = (JavaObject) value;
176    } else {
177      jo = new JavaObject(value);
178    }
179    s.setSymbolValue(jo);
180    return jo;
181  }
182
183  public Symbol findSymbol(String name, String pkg) {
184    Cons values = (Cons) (interpreter.eval("(cl:multiple-value-list (find-symbol (symbol-name '#:"
185                         + escape(name) + ")" + (pkg == null ? "" : " :" + escape(pkg))
186                         + "))"));
187    if(values.cadr() == Lisp.NIL) {
188      return null;
189    } else {
190      return (Symbol) values.car();
191    }
192  }
193
194  public Symbol findSymbol(String name) {
195    //Known bug: doesn't handle escaped ':' e.g. |a:b|
196    int i = name.indexOf(':');
197    if(i < 0) { 
198      return findSymbol(name, null);
199    } else {
200        if((i < name.length() - 1) && (name.charAt(i + 1) == ':')) {
201      return findSymbol(name.substring(i + 2), name.substring(0, i));
202        } else {
203      return findSymbol(name.substring(i + 1), name.substring(0, i));
204        }
205    }
206  }
207 
208  public Function findFunction(String name) {
209    return (Function) interpreter.eval("#'" + name);
210  }
211
212  @Override
213  public Bindings createBindings() {
214    return new SimpleBindings();
215  }
216
217    private static LispObject makeBindings(Bindings bindings) {
218    if (bindings == null || bindings.size() == 0) {
219      return Lisp.NIL;
220    }
221    LispObject[] argList = new LispObject[bindings.size()];
222    int i = 0;
223    for (Map.Entry<String, Object> entry : bindings.entrySet()) {
224      argList[i++] = Symbol.CONS.execute(new SimpleString(entry.getKey()),
225                 JavaObject.getInstance(entry.getValue(), true));
226    }
227    return Symbol.LIST.getSymbolFunction().execute(argList);
228  }
229
230    Object eval(Function evaluator, LispObject code, ScriptContext ctx) throws ScriptException {
231  LispObject retVal = null;
232      Stream outStream = new Stream(Symbol.SYSTEM_STREAM, ctx.getWriter());
233      Stream inStream  = new Stream(Symbol.SYSTEM_STREAM, ctx.getReader());
234      retVal = evaluator.execute(makeBindings(ctx.getBindings(ScriptContext.GLOBAL_SCOPE)),
235               makeBindings(ctx.getBindings(ScriptContext.ENGINE_SCOPE)),
236               inStream, outStream,
237               code, new JavaObject(ctx));
238      return retVal.javaInstance();
239    }
240 
241  @Override
242  public Object eval(String code, ScriptContext ctx) throws ScriptException {
243    return eval(evalScript, new SimpleString(code), ctx);
244  }
245
246  private static String toString(Reader reader) throws IOException {
247    StringWriter w = new StringWriter();
248    int i;
249    i = reader.read();
250    while (i != -1) {
251      w.write(i);
252      i = reader.read();
253    }
254    return w.toString();
255  }
256 
257  @Override
258  public Object eval(Reader code, ScriptContext ctx) throws ScriptException {
259    try {
260      return eval(toString(code), ctx);
261    } catch (IOException e) {
262      return new ScriptException(e);
263    }
264  }
265
266  @Override
267  public ScriptEngineFactory getFactory() {
268    return new AbclScriptEngineFactory();
269  }
270 
271  public <T> T getInterface(Class<T> clasz) {
272    try {
273      return getInterface(eval("(cl:find-package '#:ABCL-SCRIPT-USER)"), clasz);
274    } catch (ScriptException e) {
275      throw new Error(e);
276    }
277  }
278
279  @SuppressWarnings("unchecked")
280  public <T> T getInterface(Object thiz, Class<T> clasz) {
281      Symbol s = findSymbol("jmake-proxy", "JAVA");
282      JavaObject iface = new JavaObject(clasz);
283      return (T) ((JavaObject) s.execute(iface, (LispObject) thiz)).javaInstance();
284  }
285 
286    public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException {
287  Symbol s;
288  if(name.indexOf(':') >= 0) {
289      s = findSymbol(name);
290  } else {
291      s = findSymbol(name, "ABCL-SCRIPT-USER");
292  }
293  if(s != null) {
294      LispObject f = s.getSymbolFunction();
295      if(f != null && f instanceof Function) {
296    LispObject functionAndArgs = Lisp.NIL.push(f);
297    for(int i = 0; i < args.length; ++i) {
298        functionAndArgs = functionAndArgs.push(JavaObject.getInstance(args[i], true));
299    }
300    functionAndArgs = functionAndArgs.reverse();
301    return eval(evalFunction, functionAndArgs, getContext());
302      } else {
303    throw new NoSuchMethodException(name);
304      }
305  } else {
306      throw new NoSuchMethodException(name);
307  }
308    }
309
310    public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptException, NoSuchMethodException {
311  throw new UnsupportedOperationException("Common Lisp does not have methods in the Java sense. Use invokeFunction instead.");
312    }
313
314    public class AbclCompiledScript extends CompiledScript {
315
316  private LispObject function;
317 
318  public AbclCompiledScript(LispObject function) {
319      this.function = function;
320  }
321 
322  @Override
323  public Object eval(ScriptContext context) throws ScriptException {
324      return AbclScriptEngine.this.eval(evalCompiledScript, function, context);
325  }
326 
327  @Override
328  public ScriptEngine getEngine() {
329      return AbclScriptEngine.this;
330  }
331 
332    }
333
334 
335  @Override
336  public CompiledScript compile(String script) throws ScriptException {
337    try {
338        Function f = (Function) compileScript.execute(new SimpleString(script));
339        return new AbclCompiledScript(f);
340    } catch(ClassCastException e) {
341      throw new ScriptException(e);
342    }
343  }
344
345  @Override
346  public CompiledScript compile(Reader script) throws ScriptException {
347    try {
348      return compile(toString(script));
349    } catch (IOException e) {
350      throw new ScriptException(e);
351    }
352  }
353
354}
Note: See TracBrowser for help on using the repository browser.