Open Source Repository

Home /beanshell/bsh-2.0b4 | Repository Home



bsh/Interpreter.java
/*****************************************************************************
 *                                                                           *
 *  This file is part of the BeanShell Java Scripting distribution.          *
 *  Documentation and updates may be found at http://www.beanshell.org/      *
 *                                                                           *
 *  Sun Public License Notice:                                               *
 *                                                                           *
 *  The contents of this file are subject to the Sun Public License Version  *
 *  1.0 (the "License"); you may not use this file except in compliance with *
 *  the License. A copy of the License is available at http://www.sun.com    * 
 *                                                                           *
 *  The Original Code is BeanShell. The Initial Developer of the Original    *
 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
 *  (C) 2000.  All Rights Reserved.                                          *
 *                                                                           *
 *  GNU Public License Notice:                                               *
 *                                                                           *
 *  Alternatively, the contents of this file may be used under the terms of  *
 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
 *  provisions of LGPL are applicable instead of those above. If you wish to *
 *  allow use of your version of this file only under the  terms of the LGPL *
 *  and not to allow others to use your version of this file under the SPL,  *
 *  indicate your decision by deleting the provisions above and replace      *
 *  them with the notice and other provisions required by the LGPL.  If you  *
 *  do not delete the provisions above, a recipient may use your version of  *
 *  this file under either the SPL or the LGPL.                              *
 *                                                                           *
 *  Patrick Niemeyer ([email protected])                                           *
 *  Author of Learning Java, O'Reilly & Associates                           *
 *  http://www.pat.net/~pat/                                                 *
 *                                                                           *
 *****************************************************************************/

package bsh;

import java.util.Vector;
import java.io.*;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

/**
  The BeanShell script interpreter.

  An instance of Interpreter can be used to source scripts and evaluate 
  statements or expressions.  
  <p>
  Here are some examples:

  <p><blockquote><pre>
    Interpeter bsh = new Interpreter();

    // Evaluate statements and expressions
    bsh.eval("foo=Math.sin(0.5)");
    bsh.eval("bar=foo*5; bar=Math.cos(bar);");
    bsh.eval("for(i=0; i<10; i++) { print(\"hello\"); }");
    // same as above using java syntax and apis only
    bsh.eval("for(int i=0; i<10; i++) { System.out.println(\"hello\"); }");

    // Source from files or streams
    bsh.source("myscript.bsh");  // or bsh.eval("source(\"myscript.bsh\")");

    // Use set() and get() to pass objects in and out of variables
    bsh.set( "date", new Date() );
    Date date = (Date)bsh.get( "date" );
    // This would also work:
    Date date = (Date)bsh.eval( "date" );

    bsh.eval("year = date.getYear()");
    Integer year = (Integer)bsh.get("year");  // primitives use wrappers

    // With Java1.3+ scripts can implement arbitrary interfaces...
    // Script an awt event handler (or source it from a file, more likely)
    bsh.eval( "actionPerformed( e ) { print( e ); }");
    // Get a reference to the script object (implementing the interface)
    ActionListener scriptedHandler = 
      (ActionListener)bsh.eval("return (ActionListener)this");
    // Use the scripted event handler normally...
    new JButton.addActionListener( script );
  </pre></blockquote>
  <p>

  In the above examples we showed a single interpreter instance, however 
  you may wish to use many instances, depending on the application and how
  you structure your scripts.  Interpreter instances are very light weight
  to create, however if you are going to execute the same script repeatedly
  and require maximum performance you should consider scripting the code as 
  a method and invoking the scripted method each time on the same interpreter
  instance (using eval()). 
  <p>

  See the BeanShell User's Manual for more information.
*/
public class Interpreter 
  implements Runnable, ConsoleInterface,Serializable
{
  /* --- Begin static members --- */

  public static final String VERSION = "2.0b5";
  /*
    Debug utils are static so that they are reachable by code that doesn't
    necessarily have an interpreter reference (e.g. tracing in utils).
    In the future we may want to allow debug/trace to be turned on on
    a per interpreter basis, in which case we'll need to use the parent 
    reference in some way to determine the scope of the command that 
    turns it on or off.
  */
    public static boolean DEBUG, TRACE, LOCALSCOPING;

  // This should be per instance
    transient static PrintStream debug;
  static String systemLineSeparator = "\n"// default

  static 
    staticInit();
  }

  /** Shared system object visible under bsh.system */
  static This sharedObject;

  /** 
    Strict Java mode 
    @see #setStrictJava( boolean )
  */
  private boolean strictJava = false;

  /* --- End static members --- */

  /* --- Instance data --- */

  transient Parser parser;
    NameSpace globalNameSpace;
    transient Reader in;
    transient PrintStream out;
    transient PrintStream err;
    ConsoleInterface console; 

  /** If this interpeter is a child of another, the parent */
  Interpreter parent;

  /** The name of the file or other source that this interpreter is reading */
  String sourceFileInfo;

  /** by default in interactive mode System.exit() on EOF */
  private boolean exitOnEOF = true;

    protected boolean 
    evalOnly,     // Interpreter has no input stream, use eval() only
    interactive;  // Interpreter has a user, print prompts, etc.

  /** Control the verbose printing of results for the show() command. */
  private boolean showResults;

  /* --- End instance data --- */

  /**
    The main constructor.
    All constructors should now pass through here.

    @param namespace If namespace is non-null then this interpreter's 
    root namespace will be set to the one provided.  If it is null a new 
    one will be created for it.
    @param parent The parent interpreter if this interpreter is a child 
      of another.  May be null.  Children share a BshClassManager with
      their parent instance.
    @param sourceFileInfo An informative string holding the filename 
    or other description of the source from which this interpreter is
    reading... used for debugging.  May be null.
  */
    public Interpreter(
    Reader in, PrintStream out, PrintStream err, 
    boolean interactive, NameSpace namespace,
    Interpreter parent, String sourceFileInfo )
    {
    //System.out.println("New Interpreter: "+this +", sourcefile = "+sourceFileInfo );
    parser = new Parserin );
    long t1=System.currentTimeMillis();
        this.in = in;
        this.out = out;
        this.err = err;
        this.interactive = interactive;
    debug = err;
    this.parent = parent;
    if parent != null )
      setStrictJavaparent.getStrictJava() );
    this.sourceFileInfo = sourceFileInfo;

    BshClassManager bcm = BshClassManager.createClassManagerthis );
    if namespace == null )
          this.globalNameSpace = new NameSpacebcm, "global");
    else
      this.globalNameSpace = namespace;

    // now done in NameSpace automatically when root
    // The classes which are imported by default
    //globalNameSpace.loadDefaultImports();

    /* 
      Create the root "bsh" system object if it doesn't exist.
    */
    if getu("bsh"instanceof bsh.This ) )
      initRootSystemObject();

    if interactive )
      loadRCFiles();

    long t2=System.currentTimeMillis();
    if Interpreter.DEBUG 
      Interpreter.debug("Time to initialize interpreter: "+(t2-t1));
    }

    public Interpreter(
    Reader in, PrintStream out, PrintStream err, 
    boolean interactive, NameSpace namespace)
    {
    thisin, out, err, interactive, namespace, null, null );
  }

    public Interpreter(
    Reader in, PrintStream out, PrintStream err, boolean interactive)
    {
        this(in, out, err, interactive, null);
    }

  /**
    Construct a new interactive interpreter attached to the specified 
    console using the specified parent namespace.
  */
    public Interpreter(ConsoleInterface console, NameSpace globalNameSpace) {

        thisconsole.getIn(), console.getOut(), console.getErr()
      true, globalNameSpace );

    setConsoleconsole );
    }

  /**
    Construct a new interactive interpreter attached to the specified 
    console.
  */
    public Interpreter(ConsoleInterface console) {
        this(console, null);
    }

  /**
    Create an interpreter for evaluation only.
  */
    public Interpreter()
    {
    thisnew StringReader("")
      System.out, System.err, false, null );
        evalOnly = true;
    setu"bsh.evalOnly", Primitive.TRUE );
    }

  // End constructors

  /**
    Attach a console
    Note: this method is incomplete.
  */
  public void setConsoleConsoleInterface console ) {
    this.console = console;
    setu"bsh.console", console );
    // redundant with constructor
    setOutconsole.getOut() );
    setErrconsole.getErr() );
    // need to set the input stream - reinit the parser?
  }

  private void initRootSystemObject() 
  {
    BshClassManager bcm = getClassManager();
    // bsh
    setu("bsh"new NameSpacebcm, "Bsh Object" ).getThisthis ) );

    // init the static shared sharedObject if it's not there yet
    if sharedObject == null )
      sharedObject = new NameSpace
        bcm, "Bsh Shared System Object" ).getThisthis );
    // bsh.system
    setu"bsh.system", sharedObject );
    setu"bsh.shared", sharedObject )// alias

    // bsh.help
    This helpText = new NameSpace
      bcm, "Bsh Command Help Text" ).getThisthis );
    setu"bsh.help", helpText );

    // bsh.cwd
    try {
      setu"bsh.cwd", System.getProperty("user.dir") );
    catch SecurityException e ) { 
      // applets can't see sys props
      setu"bsh.cwd""." );
    }

    // bsh.interactive
    setu"bsh.interactive", interactive ? Primitive.TRUE : Primitive.FALSE );
    // bsh.evalOnly
    setu"bsh.evalOnly", evalOnly ? Primitive.TRUE : Primitive.FALSE );
  }

  /**
    Set the global namespace for this interpreter.
    <p>

    Note: This is here for completeness.  If you're using this a lot 
    it may be an indication that you are doing more work than you have 
    to.  For example, caching the interpreter instance rather than the 
    namespace should not add a significant overhead.  No state other 
    than the debug status is stored in the interpreter.
    <p>

    All features of the namespace can also be accessed using the 
    interpreter via eval() and the script variable 'this.namespace'
    (or global.namespace as necessary).
  */
  public void setNameSpaceNameSpace globalNameSpace ) {
    this.globalNameSpace = globalNameSpace;
  }

  /**
    Get the global namespace of this interpreter.
    <p>

    Note: This is here for completeness.  If you're using this a lot 
    it may be an indication that you are doing more work than you have 
    to.  For example, caching the interpreter instance rather than the 
    namespace should not add a significant overhead.  No state other than 
    the debug status is stored in the interpreter.  
    <p>

    All features of the namespace can also be accessed using the 
    interpreter via eval() and the script variable 'this.namespace'
    (or global.namespace as necessary).
  */
  public NameSpace getNameSpace() {
    return globalNameSpace;
  }

  /**
    Run the text only interpreter on the command line or specify a file.
  */
    public static void mainString [] args 
  {
        if args.length > ) {
      String filename = args[0];

      String [] bshArgs;
      if args.length > ) {
        bshArgs = new String args.length -];
        System.arraycopyargs, 1, bshArgs, 0, args.length-);
      else
        bshArgs = new String [0];

            Interpreter interpreter = new Interpreter();
      //System.out.println("run i = "+interpreter);
      interpreter.setu"bsh.args", bshArgs );
      try {
        Object result = 
          interpreter.sourcefilename, interpreter.globalNameSpace );
        if result instanceof Class )
          try {
            invokeMain( (Class)result, bshArgs );
          catch Exception e 
          {
            Object o = e;
            if instanceof InvocationTargetException )
              o = ((InvocationTargetException)e)
                .getTargetException();
            System.err.println(
              "Class: "+result+" main method threw exception:"+o);
          }
      catch FileNotFoundException e ) {
        System.out.println("File not found: "+e);
      catch TargetError e ) {
        System.out.println("Script threw exception: "+e);
        if e.inNativeCode() )
          e.printStackTraceDEBUG, System.err );
      catch EvalError e ) {
        System.out.println("Evaluation Error: "+e);
      catch IOException e ) {
        System.out.println("I/O Error: "+e);
      }
        else 
    {
      // Workaround for JDK bug 4071281, where system.in.available() 
      // returns too large a value. This bug has been fixed in JDK 1.2.
      InputStream src;
      if System.getProperty("os.name").startsWith("Windows"
        && System.getProperty("java.version").startsWith("1.1."))
      {
        src = new FilterInputStream(System.in) {
          public int available() throws IOException {
            return 0;
          }
        };
      }
      else
        src = System.in;

            Reader in = new CommandLineReadernew InputStreamReader(src));
            Interpreter interpreter = 
        new Interpreterin, System.out, System.err, true );
          interpreter.run();
        }
    }

  public static void invokeMainClass clas, String [] args 
    throws Exception
  {
      Method main = Reflect.resolveJavaMethod(
      null/*BshClassManager*/, clas, "main"
      new Class [] { String [].class }true/*onlyStatic*/ );
    if main != null )
      main.invokenull, new Object [] { args } );
  }

  /**
    Run interactively.  (printing prompts, etc.)
  */
    public void run() 
  {
        if(evalOnly)
            throw new RuntimeException("bsh Interpreter: No stream");

        /*
          We'll print our banner using eval(String) in order to
          exercise the parser and get the basic expression classes loaded...
          This ameliorates the delay after typing the first statement.
        */
        if interactive )
      try 
        eval("printBanner();")
      catch EvalError e ) {
        println(
          "BeanShell "+VERSION+" - by Pat Niemeyer ([email protected])");
      }

    // init the callstack.  
    CallStack callstack = new CallStackglobalNameSpace );

        boolean eof = false;
        while!eof )
        {
            try
            {
                // try to sync up the console
                System.out.flush();
                System.err.flush();
                Thread.yield();  // this helps a little

                if interactive )
                    printgetBshPrompt() );

                eof = Line();

                ifget_jjtree().nodeArity() )  // number of child nodes 
                {
                    SimpleNode node = (SimpleNode)(get_jjtree().rootNode());

                    if(DEBUG)
                        node.dump(">");

                    Object ret = node.evalcallstack, this );
        
          // sanity check during development
          if callstack.depth() )
            throw new InterpreterError(
              "Callstack growing: "+callstack);

                    if(ret instanceof ReturnControl)
                        ret = ((ReturnControl)ret).value;

                    ifret != Primitive.VOID )
                    {
                        setu("$_", ret);
            if showResults )
                            println("<" + ret + ">");
                    }
                }
            }
            catch(ParseException e)
            {
                error("Parser Error: " + e.getMessage(DEBUG));
        if DEBUG )
                  e.printStackTrace();
                if(!interactive)
                    eof = true;

                parser.reInitInput(in);
            }
            catch(InterpreterError e)
            {
                error("Internal Error: " + e.getMessage());
                e.printStackTrace();
                if(!interactive)
                    eof = true;
            }
            catch(TargetError e)
            {
                error("// Uncaught Exception: " + e );
        if e.inNativeCode() )
          e.printStackTraceDEBUG, err );
                if(!interactive)
                    eof = true;
        setu("$_e", e.getTarget());
            }
            catch (EvalError e)
            {
        if interactive )
          error"EvalError: "+e.toString() );
        else
          error"EvalError: "+e.getMessage() );

                if(DEBUG)
                    e.printStackTrace();

                if(!interactive)
                    eof = true;
            }
            catch(Exception e)
            {
                error("Unknown error: " + e);
        if DEBUG )
                  e.printStackTrace();
                if(!interactive)
                    eof = true;
            }
            catch(TokenMgrError e)
            {
        error("Error parsing input: " + e);

        /*
          We get stuck in infinite loops here when unicode escapes
          fail.  Must re-init the char stream reader 
          (ASCII_UCodeESC_CharStream.java)
        */
        parser.reInitTokenInputin );

                if(!interactive)
                    eof = true;
            }
            finally
            {
                get_jjtree().reset();
        // reinit the callstack
        if callstack.depth() ) {
          callstack.clear();
          callstack.pushglobalNameSpace );
        }
            }
        }

    if interactive && exitOnEOF )
      System.exit(0);
    }

  // begin source and eval

  /**
    Read text from fileName and eval it.
  */
    public Object sourceString filename, NameSpace nameSpace 
    throws FileNotFoundException, IOException, EvalError 
  {
    File file = pathToFilefilename );
    if Interpreter.DEBUG debug("Sourcing file: "+file);
    Reader sourceIn = new BufferedReadernew FileReader(file) );
    try {
      return evalsourceIn, nameSpace, filename );
    finally {
      sourceIn.close();
    }
  }

  /**
    Read text from fileName and eval it.
    Convenience method.  Use the global namespace.
  */
    public Object sourceString filename 
    throws FileNotFoundException, IOException, EvalError 
  {
    return sourcefilename, globalNameSpace );
  }

    /**
        Spawn a non-interactive local interpreter to evaluate text in the 
    specified namespace.  

    Return value is the evaluated object (or corresponding primitive 
    wrapper).

    @param sourceFileInfo is for information purposes only.  It is used to
    display error messages (and in the future may be made available to
    the script).
    @throws EvalError on script problems
    @throws TargetError on unhandled exceptions from the script
    */
  /*
    Note: we need a form of eval that passes the callstack through...
  */
  /*
  Can't this be combined with run() ?
  run seems to have stuff in it for interactive vs. non-interactive...
  compare them side by side and see what they do differently, aside from the
  exception handling.
  */

    public Object eval
    Reader in, NameSpace nameSpace, String sourceFileInfo
      /*, CallStack callstack */ 
    throws EvalError 
  {
    Object retVal = null;
    if Interpreter.DEBUG debug("eval: nameSpace = "+nameSpace);

    /* 
      Create non-interactive local interpreter for this namespace
      with source from the input stream and out/err same as 
      this interpreter.
    */
        Interpreter localInterpreter = 
      new Interpreter
        in, out, err, false, nameSpace, this, sourceFileInfo  );

    CallStack callstack = new CallStacknameSpace );

        boolean eof = false;
        while(!eof)
        {
      SimpleNode node = null;
            try
            {
                eof = localInterpreter.Line();
                if (localInterpreter.get_jjtree().nodeArity() 0)
                {
                    node = (SimpleNode)localInterpreter.get_jjtree().rootNode();
          // quick filter for when we're running as a compiler only
          if getSaveClasses()
            && !(node instanceof BSHClassDeclaration)
            && !(node instanceof BSHImportDeclaration )
            && !(node instanceof BSHPackageDeclaration )
          )
            continue;

          // nodes remember from where they were sourced
          node.setSourceFilesourceFileInfo );

          if TRACE )
            println"// " +node.getText() );

                    retVal = node.evalcallstack, localInterpreter );

          // sanity check during development
          if callstack.depth() )
            throw new InterpreterError(
              "Callstack growing: "+callstack);

                    if retVal instanceof ReturnControl ) {
                        retVal = ((ReturnControl)retVal).value;
            break// non-interactive, return control now
          }

          if localInterpreter.showResults 
            && retVal != Primitive.VOID )
            println("<" + retVal + ">");
                }
            catch(ParseException e) {
        /*
                throw new EvalError(
          "Sourced file: "+sourceFileInfo+" parser Error: " 
          + e.getMessage( DEBUG ), node, callstack );
        */
        if DEBUG )
          // show extra "expecting..." info
          errore.getMessage(DEBUG) );

        // add the source file info and throw again
        e.setErrorSourceFilesourceFileInfo );
        throw e;

            catch InterpreterError e ) {
                e.printStackTrace();
                throw new EvalError(
          "Sourced file: "+sourceFileInfo+" internal Error: " 
          + e.getMessage(), node, callstack);
            catch TargetError e ) {
        // failsafe, set the Line as the origin of the error.
        if e.getNode()==null )
          e.setNodenode );
        e.reThrow("Sourced file: "+sourceFileInfo);
            catch EvalError e) {
                if DEBUG)
                    e.printStackTrace();
        // failsafe, set the Line as the origin of the error.
        if e.getNode()==null )
          e.setNodenode );
        e.reThrow"Sourced file: "+sourceFileInfo );
            catch Exception e) {
                if DEBUG)
                  e.printStackTrace();
                throw new EvalError(
          "Sourced file: "+sourceFileInfo+" unknown error: " 
          + e.getMessage(), node, callstack);
            catch(TokenMgrError e) {
                throw new EvalError(
          "Sourced file: "+sourceFileInfo+" Token Parsing Error: " 
          + e.getMessage(), node, callstack );
            finally {
                localInterpreter.get_jjtree().reset();

        // reinit the callstack
        if callstack.depth() ) {
          callstack.clear();
          callstack.pushnameSpace );
        }
            }
        }
    return Primitive.unwrapretVal );
    }

  /**
    Evaluate the inputstream in this interpreter's global namespace.
  */
    public Object evalReader in throws EvalError 
  {
    return evalin, globalNameSpace, "eval stream" );
  }

  /**
    Evaluate the string in this interpreter's global namespace.
  */
    public Object evalString statements throws EvalError {
    if Interpreter.DEBUG debug("eval(String): "+statements);
    return eval(statements, globalNameSpace);
  }

  /**
    Evaluate the string in the specified namespace.
  */
    public Object evalString statements, NameSpace nameSpace 
    throws EvalError 
  {

    String s = statements.endsWith(";"? statements : statements+";" );
        return eval
      new StringReader(s), nameSpace, 
      "inline evaluation of: ``"+ showEvalString(s)+"''" );
    }

  private String showEvalStringString s ) {
    s = s.replace('\n'' ');
    s = s.replace('\r'' ');
    if s.length() 80 )
      s = s.substring080 " . . . ";
    return s;
  }

  // end source and eval

  /**
    Print an error message in a standard format on the output stream
    associated with this interpreter. On the GUI console this will appear 
    in red, etc.
  */
    public final void errorObject o ) {
    if console != null )
        console.error"// Error: " + o +"\n" );
    else {
      err.println("// Error: " + o );
      err.flush();
    }
    }

  // ConsoleInterface
  // The interpreter reflexively implements the console interface that it 
  // uses.  Should clean this up by using an inner class to implement the
  // console for us.

  /** 
    Get the input stream associated with this interpreter.
    This may be be stdin or the GUI console.
  */
  public Reader getIn() { return in; }

  /** 
    Get the outptut stream associated with this interpreter.
    This may be be stdout or the GUI console.
  */
  public PrintStream getOut() { return out; }

  /** 
    Get the error output stream associated with this interpreter.
    This may be be stderr or the GUI console.
  */
  public PrintStream getErr() { return err; }

    public final void printlnObject o )
    {
        printString.valueOf(o+ systemLineSeparator );
    }

    public final void printObject o )
    {
    if (console != null) {
            console.print(o);
        else {
            out.print(o);
            out.flush();
        }
    }

  // End ConsoleInterface

  /**
    Print a debug message on debug stream associated with this interpreter
    only if debugging is turned on.
  */
    public final static void debug(String s)
    {
        if DEBUG )
            debug.println("// Debug: " + s);
    }

  /* 
    Primary interpreter set and get variable methods
    Note: These are squeltching errors... should they?
  */

  /**
    Get the value of the name.
    name may be any value. e.g. a variable or field
  */
    public Object getString name throws EvalError
  {
    try {
      Object ret = globalNameSpace.getname, this );
      return Primitive.unwrapret );
    catch UtilEvalError e ) {
      throw e.toEvalErrorSimpleNode.JAVACODE, new CallStack() );
    }
  }

  /**
    Unchecked get for internal use
  */
    Object getuString name ) {
    try 
      return getname );
    catch EvalError e ) { 
      throw new InterpreterError("set: "+e);
    }
  }

  /**
    Assign the value to the name.  
    name may evaluate to anything assignable. e.g. a variable or field.
  */
    public void setString name, Object value )
    throws EvalError 
  {
    // map null to Primtive.NULL coming in...
    if value == null )
      value = Primitive.NULL;

    CallStack callstack = new CallStack();
    try {
      if Name.isCompoundname ) ) 
      {
        LHS lhs = globalNameSpace.getNameResolvername ).toLHS
          callstack, this );
        lhs.assignvalue, false );
      else // optimization for common case
        globalNameSpace.setVariablename, value, false );
    catch UtilEvalError e ) { 
      throw e.toEvalErrorSimpleNode.JAVACODE, callstack )
    }
  }

  /**
    Unchecked set for internal use
  */
    void setu(String name, Object value) {
    try 
      set(name, value);
    catch EvalError e ) { 
      throw new InterpreterError("set: "+e);
    }
  }

    public void set(String name, long valuethrows EvalError {
        set(name, new Primitive(value));
  }
    public void set(String name, int valuethrows EvalError {
        set(name, new Primitive(value));
  }
    public void set(String name, double valuethrows EvalError {
        set(name, new Primitive(value));
  }
    public void set(String name, float valuethrows EvalError {
        set(name, new Primitive(value));
  }
    public void set(String name, boolean valuethrows EvalError {
        set(name, value ? Primitive.TRUE : Primitive.FALSE);
  }

  /**
    Unassign the variable name.  
    Name should evaluate to a variable.
  */
    public void unsetString name 
    throws EvalError 
  {
    /*
      We jump through some hoops here to handle arbitrary cases like
      unset("bsh.foo");
    */
    CallStack callstack = new CallStack();
    try {
      LHS lhs = globalNameSpace.getNameResolvername ).toLHS
        callstack, this );

      if lhs.type != LHS.VARIABLE )
        throw new EvalError("Can't unset, not a variable: "+name, 
          SimpleNode.JAVACODE, new CallStack() );

      //lhs.assign( null, false );
      lhs.nameSpace.unsetVariablename );
    catch UtilEvalError e ) {
      throw new EvalErrore.getMessage()
        SimpleNode.JAVACODE, new CallStack() );
    }
  }

  // end primary set and get methods

  /**
    Get a reference to the interpreter (global namespace), cast 
    to the specified interface type.  Assuming the appropriate 
    methods of the interface are defined in the interpreter, then you may 
    use this interface from Java, just like any other Java object.
    <p>

    For example:
    <pre>
      Interpreter interpreter = new Interpreter();
      // define a method called run()
      interpreter.eval("run() { ... }");
    
      // Fetch a reference to the interpreter as a Runnable
      Runnable runnable = 
        (Runnable)interpreter.getInterface( Runnable.class );
    </pre>
    <p>

    Note that the interpreter does *not* require that any or all of the
    methods of the interface be defined at the time the interface is
    generated.  However if you attempt to invoke one that is not defined
    you will get a runtime exception.
    <p>

    Note also that this convenience method has exactly the same effect as 
    evaluating the script:
    <pre>
      (Type)this;
    </pre>
    <p>

    For example, the following is identical to the previous example:
    <p>

    <pre>
      // Fetch a reference to the interpreter as a Runnable
      Runnable runnable = 
        (Runnable)interpreter.eval( "(Runnable)this" );
    </pre>
    <p>

    <em>Version requirement</em> Although standard Java interface types 
    are always available, to be used with arbitrary interfaces this 
    feature requires that you are using Java 1.3 or greater.
    <p>

    @throws EvalError if the interface cannot be generated because the
    version of Java does not support the proxy mechanism. 
  */
  public Object getInterfaceClass interf throws EvalError
  {
    try {
      return globalNameSpace.getThisthis ).getInterfaceinterf );
    catch UtilEvalError e ) {
      throw e.toEvalErrorSimpleNode.JAVACODE, new CallStack() );
    }
  }

  /*  Methods for interacting with Parser */

  private JJTParserState get_jjtree() {
    return parser.jjtree;
  }

    private JavaCharStream get_jj_input_stream() {
    return parser.jj_input_stream;
  }

    private boolean Line() throws ParseException {
    return parser.Line();
  }

  /*  End methods for interacting with Parser */

  void loadRCFiles() {
    try {
      String rcfile = 
        // Default is c:\windows under win98, $HOME under Unix
        System.getProperty("user.home"+ File.separator + ".bshrc";
      sourcercfile, globalNameSpace );
    catch Exception e ) { 
      // squeltch security exception, filenotfoundexception
      if Interpreter.DEBUG debug("Could not find rc file: "+e);
    }
  }

  /**
    Localize a path to the file name based on the bsh.cwd interpreter 
    working directory.
  */
    public File pathToFileString fileName 
    throws IOException
  {
    File file = new FilefileName );

    // if relative, fix up to bsh.cwd
    if !file.isAbsolute() ) {
      String cwd = (String)getu("bsh.cwd");
      file = new Filecwd + File.separator + fileName );
    }

    // The canonical file name is also absolute.
    // No need for getAbsolutePath() here...
    return new Filefile.getCanonicalPath() );
  }

  public static void redirectOutputToFileString filename 
  {
    try {
      PrintStream pout = new PrintStream
        new FileOutputStreamfilename ) );
      System.setOutpout );
      System.setErrpout );
    catch IOException e ) {
      System.err.println("Can't redirect output to file: "+filename );
    }
  }

  /**
    Set an external class loader to be used as the base classloader
    for BeanShell.  The base classloader is used for all classloading 
    unless/until the addClasspath()/setClasspath()/reloadClasses()  
    commands are called to modify the interpreter's classpath.  At that 
    time the new paths /updated paths are added on top of the base 
    classloader.
    <p>

    BeanShell will use this at the same point it would otherwise use the 
    plain Class.forName().
    i.e. if no explicit classpath management is done from the script
    (addClassPath(), setClassPath(), reloadClasses()) then BeanShell will
    only use the supplied classloader.  If additional classpath management
    is done then BeanShell will perform that in addition to the supplied
    external classloader.  
    However BeanShell is not currently able to reload
    classes supplied through the external classloader.
    <p>

    @see BshClassManager#setClassLoader( ClassLoader )
  */
  public void setClassLoaderClassLoader externalCL ) {
    getClassManager().setClassLoaderexternalCL );
  }

  /**
    Get the class manager associated with this interpreter
    (the BshClassManager of this interpreter's global namespace).
    This is primarily a convenience method.
  */
  public BshClassManager getClassManager() 
  {
    return getNameSpace().getClassManager();
  }
  
  /**
    Set strict Java mode on or off.  
    This mode attempts to make BeanShell syntax behave as Java
    syntax, eliminating conveniences like loose variables, etc.
    When enabled, variables are required to be declared or initialized 
    before use and method arguments are reqired to have types. 
    <p>

    This mode will become more strict in a future release when 
    classes are interpreted and there is an alternative to scripting
    objects as method closures.
  */
  public void setStrictJavaboolean ) { 
    this.strictJava = b; 
  }

  /**
    @see #setStrictJava( boolean )
  */
  public boolean getStrictJava() { 
    return this.strictJava;
  }

  static void staticInit() 
  {
  /* 
    Apparently in some environments you can't catch the security exception
    at all...  e.g. as an applet in IE  ... will probably have to work 
    around 
  */
    try {
      systemLineSeparator = System.getProperty("line.separator");
        debug = System.err;
        DEBUG = Boolean.getBoolean("debug");
        TRACE = Boolean.getBoolean("trace");
        LOCALSCOPING = Boolean.getBoolean("localscoping");
      String outfilename = System.getProperty("outfile");
      if outfilename != null )
        redirectOutputToFileoutfilename );
    catch SecurityException e ) { 
      System.err.println("Could not init static:"+e);
    catch Exception e ) {
      System.err.println("Could not init static(2):"+e);
    catch Throwable e ) { 
      System.err.println("Could not init static(3):"+e);
    }
  }

  /**
    Specify the source of the text from which this interpreter is reading.
    Note: there is a difference between what file the interrpeter is 
    sourcing and from what file a method was originally parsed.  One
    file may call a method sourced from another file.  See SimpleNode
    for origination file info.
    @see bsh.SimpleNode#getSourceFile()
  */
  public String getSourceFileInfo() { 
    if sourceFileInfo != null )
      return sourceFileInfo;
    else
      return "<unknown source>";
  }

  /**
    Get the parent Interpreter of this interpreter, if any.
    Currently this relationship implies the following:
      1) Parent and child share a BshClassManager
      2) Children indicate the parent's source file information in error
      reporting.
    When created as part of a source() / eval() the child also shares
    the parent's namespace.  But that is not necessary in general.
  */
  public Interpreter getParent() {
    return parent;
  }

  public void setOutPrintStream out ) {
    this.out = out;
  }
  public void setErrPrintStream err ) {
    this.err = err;
  }

  /**
    De-serialization setup.
    Default out and err streams to stdout, stderr if they are null.
  */
  private void readObject(ObjectInputStream stream
    throws java.io.IOException, ClassNotFoundException
  {
    stream.defaultReadObject();

    // set transient fields
    if console != null ) {
      setOutconsole.getOut() );
      setErrconsole.getErr() );
    else {
      setOutSystem.out );
      setErrSystem.err );
    }
  }

  /**
    Get the prompt string defined by the getBshPrompt() method in the
    global namespace.  This may be from the getBshPrompt() command or may
    be defined by the user as with any other method.
    Defaults to "bsh % " if the method is not defined or there is an error.
  */
  private String getBshPrompt() 
  {
    try {
      return (String)eval("getBshPrompt()");
    catch Exception e ) {
      return "bsh % ";
    }
  }

  /**
    Specify whether, in interactive mode, the interpreter exits Java upon
    end of input.  If true, when in interactive mode the interpreter will
    issue a System.exit(0) upon eof.  If false the interpreter no
    System.exit() will be done.
    <p/>
    Note: if you wish to cause an EOF externally you can try closing the
    input stream.  This is not guaranteed to work in older versions of Java
    due to Java limitations, but should work in newer JDK/JREs.  (That was
    the motivation for the Java NIO package).
  */
  public void setExitOnEOFboolean value ) {
    exitOnEOF = value; // ug
  }

  /**
    Turn on/off the verbose printing of results as for the show()
     command.
     If this interpreter has a parent the call is delegated.
     See the BeanShell show() command.
  */
  public void setShowResultsboolean showResults ) {
    this.showResults = showResults;
  }
  /**
   Show on/off verbose printing status for the show() command.
   See the BeanShell show() command.
   If this interpreter has a parent the call is delegated.
   */
  public boolean getShowResults()  {
    return showResults;
  }

  public static String getSaveClassesDir() {
    return System.getProperty("saveClasses");
  }

  public static boolean getSaveClasses()  {
    return getSaveClassesDir() != null;
  }
}