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

Last change on this file was 12749, checked in by Mark Evenson, 14 years ago

Undo previous commmit.

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