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

Last change on this file was 14002, checked in by Mark Evenson, 12 years ago

dmiles: SYS:*COMPILE-FILE-CLASS-EXTENSION* contains PATHNAME TYPE of compiled JVM artifacts.

The default "cls" of compiled JVM artifacts was chosen to easily
differentiate bewtween JVM artifacts not produced by ABCL and those
which are the JVM bytecode of the ABCL Java 5.0 compiler. During the
bootstrapping and subsequent debugging of the current compiler, this
distinction has proven more useful than giving ABCL produced artifacts
the default "class" CL:PATHNAME TYPE.

This change facilitates the bootstrapping of [running ABCL on the MSFT
.NET CLR underway by dmiles][abcl-ikvm]

[abcl-ikvm]: http://code.google.com/r/logicmoo-abcl-ikvm

dmiles: Implementation of ticket #34.

dmiles: It makes no change at first but makes implmentation satisfactory to my
initial request.

  • Property svn:eol-style set to LF
File size: 10.9 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    final String compiledExt = "." + Lisp._COMPILE_FILE_TYPE_.symbolValue().getStringValue();
140    if (filespec.endsWith(compiledExt)) {
141      return true;
142    }
143    File source;
144    File compiled;
145    if (filespec.endsWith(".lisp")) {
146      source = new File(filespec);
147      compiled = new File(filespec.substring(0, filespec.length() - 5)
148          + compiledExt);
149    } else {
150      source = new File(filespec + ".lisp");
151      compiled = new File(filespec + compiledExt);
152    }
153    if (!source.exists()) {
154      throw new IllegalArgumentException("The source file " + filespec + " cannot be found");
155    }
156    return compiled.exists()
157        && compiled.lastModified() >= source.lastModified();
158  }
159
160  public LispObject compileFile(String filespec) {
161    return interpreter.eval("(compile-file \"" + escape(filespec) + "\")");
162  }
163
164  public LispObject compileAndLoad(String filespec) {
165    return interpreter.eval("(load (compile-file \"" + escape(filespec) + "\"))");
166  }
167
168  public static boolean functionp(LispObject obj) {
169    return obj instanceof Function;
170  }
171
172  public JavaObject jsetq(String symbol, Object value) {
173    Symbol s = findSymbol(symbol);
174    JavaObject jo;
175    if (value instanceof JavaObject) {
176      jo = (JavaObject) value;
177    } else {
178      jo = new JavaObject(value);
179    }
180    s.setSymbolValue(jo);
181    return jo;
182  }
183
184  public Symbol findSymbol(String name, String pkg) {
185    Cons values = (Cons) (interpreter.eval("(cl:multiple-value-list (find-symbol (symbol-name '#:"
186                         + escape(name) + ")" + (pkg == null ? "" : " :" + escape(pkg))
187                         + "))"));
188    if(values.cadr() == Lisp.NIL) {
189      return null;
190    } else {
191      return (Symbol) values.car();
192    }
193  }
194
195  public Symbol findSymbol(String name) {
196    //Known bug: doesn't handle escaped ':' e.g. |a:b|
197    int i = name.indexOf(':');
198    if(i < 0) { 
199      return findSymbol(name, null);
200    } else {
201        if((i < name.length() - 1) && (name.charAt(i + 1) == ':')) {
202      return findSymbol(name.substring(i + 2), name.substring(0, i));
203        } else {
204      return findSymbol(name.substring(i + 1), name.substring(0, i));
205        }
206    }
207  }
208 
209  public Function findFunction(String name) {
210    return (Function) interpreter.eval("#'" + name);
211  }
212
213  @Override
214  public Bindings createBindings() {
215    return new SimpleBindings();
216  }
217
218    private static LispObject makeBindings(Bindings bindings) {
219    if (bindings == null || bindings.size() == 0) {
220      return Lisp.NIL;
221    }
222    LispObject[] argList = new LispObject[bindings.size()];
223    int i = 0;
224    for (Map.Entry<String, Object> entry : bindings.entrySet()) {
225      argList[i++] = Symbol.CONS.execute(new SimpleString(entry.getKey()),
226                 JavaObject.getInstance(entry.getValue(), true));
227    }
228    return Symbol.LIST.getSymbolFunction().execute(argList);
229  }
230
231    Object eval(Function evaluator, LispObject code, ScriptContext ctx) throws ScriptException {
232  LispObject retVal = null;
233      Stream outStream = new Stream(Symbol.SYSTEM_STREAM, ctx.getWriter());
234      Stream inStream  = new Stream(Symbol.SYSTEM_STREAM, ctx.getReader());
235      retVal = evaluator.execute(makeBindings(ctx.getBindings(ScriptContext.GLOBAL_SCOPE)),
236               makeBindings(ctx.getBindings(ScriptContext.ENGINE_SCOPE)),
237               inStream, outStream,
238               code, new JavaObject(ctx));
239      return retVal.javaInstance();
240    }
241 
242  @Override
243  public Object eval(String code, ScriptContext ctx) throws ScriptException {
244    return eval(evalScript, new SimpleString(code), ctx);
245  }
246
247  private static String toString(Reader reader) throws IOException {
248    StringWriter w = new StringWriter();
249    int i;
250    i = reader.read();
251    while (i != -1) {
252      w.write(i);
253      i = reader.read();
254    }
255    return w.toString();
256  }
257 
258  @Override
259  public Object eval(Reader code, ScriptContext ctx) throws ScriptException {
260    try {
261      return eval(toString(code), ctx);
262    } catch (IOException e) {
263      return new ScriptException(e);
264    }
265  }
266
267  @Override
268  public ScriptEngineFactory getFactory() {
269    return new AbclScriptEngineFactory();
270  }
271 
272  public <T> T getInterface(Class<T> clasz) {
273    try {
274      return getInterface(eval("(cl:find-package '#:ABCL-SCRIPT-USER)"), clasz);
275    } catch (ScriptException e) {
276      throw new Error(e);
277    }
278  }
279
280  @SuppressWarnings("unchecked")
281  public <T> T getInterface(Object thiz, Class<T> clasz) {
282      Symbol s = findSymbol("jmake-proxy", "JAVA");
283      JavaObject iface = new JavaObject(clasz);
284      return (T) ((JavaObject) s.execute(iface, (LispObject) thiz)).javaInstance();
285  }
286 
287    public Object invokeFunction(String name, Object... args) throws ScriptException, NoSuchMethodException {
288  Symbol s;
289  if(name.indexOf(':') >= 0) {
290      s = findSymbol(name);
291  } else {
292      s = findSymbol(name, "ABCL-SCRIPT-USER");
293  }
294  if(s != null) {
295      LispObject f = s.getSymbolFunction();
296      if(f != null && f instanceof Function) {
297    LispObject functionAndArgs = Lisp.NIL.push(f);
298    for(int i = 0; i < args.length; ++i) {
299        functionAndArgs = functionAndArgs.push(JavaObject.getInstance(args[i], true));
300    }
301    functionAndArgs = functionAndArgs.reverse();
302    return eval(evalFunction, functionAndArgs, getContext());
303      } else {
304    throw new NoSuchMethodException(name);
305      }
306  } else {
307      throw new NoSuchMethodException(name);
308  }
309    }
310
311    public Object invokeMethod(Object thiz, String name, Object... args) throws ScriptException, NoSuchMethodException {
312  throw new UnsupportedOperationException("Common Lisp does not have methods in the Java sense. Use invokeFunction instead.");
313    }
314
315    public class AbclCompiledScript extends CompiledScript {
316
317  private LispObject function;
318 
319  public AbclCompiledScript(LispObject function) {
320      this.function = function;
321  }
322 
323  @Override
324  public Object eval(ScriptContext context) throws ScriptException {
325      return AbclScriptEngine.this.eval(evalCompiledScript, function, context);
326  }
327 
328  @Override
329  public ScriptEngine getEngine() {
330      return AbclScriptEngine.this;
331  }
332 
333    }
334
335 
336  @Override
337  public CompiledScript compile(String script) throws ScriptException {
338    try {
339        Function f = (Function) compileScript.execute(new SimpleString(script));
340        return new AbclCompiledScript(f);
341    } catch(ClassCastException e) {
342      throw new ScriptException(e);
343    }
344  }
345
346  @Override
347  public CompiledScript compile(Reader script) throws ScriptException {
348    try {
349      return compile(toString(script));
350    } catch (IOException e) {
351      throw new ScriptException(e);
352    }
353  }
354
355}
Note: See TracBrowser for help on using the repository browser.