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

Last change on this file was 12021, checked in by astalla, 16 years ago

Used javaObject.getInstance(x, true) instead of ad-hoc toLisp method which did
basically the same thing.

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