PageRenderTime 620ms CodeModel.GetById 543ms app.highlight 65ms RepoModel.GetById 0ms app.codeStats 1ms

/jEdit/tags/jedit-4-2-pre4/bsh/Interpreter.java

#
Java | 1188 lines | 596 code | 125 blank | 467 comment | 76 complexity | 8c1fa62b19bceb398bf70a8dee6575f5 MD5 | raw file
   1/*****************************************************************************
   2 *                                                                           *
   3 *  This file is part of the BeanShell Java Scripting distribution.          *
   4 *  Documentation and updates may be found at http://www.beanshell.org/      *
   5 *                                                                           *
   6 *  Sun Public License Notice:                                               *
   7 *                                                                           *
   8 *  The contents of this file are subject to the Sun Public License Version  *
   9 *  1.0 (the "License"); you may not use this file except in compliance with *
  10 *  the License. A copy of the License is available at http://www.sun.com    * 
  11 *                                                                           *
  12 *  The Original Code is BeanShell. The Initial Developer of the Original    *
  13 *  Code is Pat Niemeyer. Portions created by Pat Niemeyer are Copyright     *
  14 *  (C) 2000.  All Rights Reserved.                                          *
  15 *                                                                           *
  16 *  GNU Public License Notice:                                               *
  17 *                                                                           *
  18 *  Alternatively, the contents of this file may be used under the terms of  *
  19 *  the GNU Lesser General Public License (the "LGPL"), in which case the    *
  20 *  provisions of LGPL are applicable instead of those above. If you wish to *
  21 *  allow use of your version of this file only under the  terms of the LGPL *
  22 *  and not to allow others to use your version of this file under the SPL,  *
  23 *  indicate your decision by deleting the provisions above and replace      *
  24 *  them with the notice and other provisions required by the LGPL.  If you  *
  25 *  do not delete the provisions above, a recipient may use your version of  *
  26 *  this file under either the SPL or the LGPL.                              *
  27 *                                                                           *
  28 *  Patrick Niemeyer (pat@pat.net)                                           *
  29 *  Author of Learning Java, O'Reilly & Associates                           *
  30 *  http://www.pat.net/~pat/                                                 *
  31 *                                                                           *
  32 *****************************************************************************/
  33
  34package bsh;
  35
  36import java.util.Vector;
  37import java.io.*;
  38
  39/**
  40	The BeanShell script interpreter.
  41
  42	An instance of Interpreter can be used to source scripts and evaluate 
  43	statements or expressions.  
  44	<p>
  45	Here are some examples:
  46
  47	<p><blockquote><pre>
  48		Interpeter bsh = new Interpreter();
  49
  50		// Evaluate statements and expressions
  51		bsh.eval("foo=Math.sin(0.5)");
  52		bsh.eval("bar=foo*5; bar=Math.cos(bar);");
  53		bsh.eval("for(i=0; i&lt;10; i++) { print(\"hello\"); }");
  54		// same as above using java syntax and apis only
  55		bsh.eval("for(int i=0; i&lt;10; i++) { System.out.println(\"hello\"); }");
  56
  57		// Source from files or streams
  58		bsh.source("myscript.bsh");  // or bsh.eval("source(\"myscript.bsh\")");
  59
  60		// Use set() and get() to pass objects in and out of variables
  61		bsh.set( "date", new Date() );
  62		Date date = (Date)bsh.get( "date" );
  63		// This would also work:
  64		Date date = (Date)bsh.eval( "date" );
  65
  66		bsh.eval("year = date.getYear()");
  67		Integer year = (Integer)bsh.get("year");  // primitives use wrappers
  68
  69		// With Java1.3+ scripts can implement arbitrary interfaces...
  70		// Script an awt event handler (or source it from a file, more likely)
  71		bsh.eval( "actionPerformed( e ) { print( e ); }");
  72		// Get a reference to the script object (implementing the interface)
  73		ActionListener scriptedHandler = 
  74			(ActionListener)bsh.eval("return (ActionListener)this");
  75		// Use the scripted event handler normally...
  76		new JButton.addActionListener( script );
  77	</pre></blockquote>
  78	<p>
  79
  80	In the above examples we showed a single interpreter instance, however 
  81	you may wish to use many instances, depending on the application and how
  82	you structure your scripts.  Interpreter instances are very light weight
  83	to create, however if you are going to execute the same script repeatedly
  84	and require maximum performance you should consider scripting the code as 
  85	a method and invoking the scripted method each time on the same interpreter
  86	instance (using eval()). 
  87	<p>
  88
  89	See the BeanShell User's Manual for more information.
  90*/
  91public class Interpreter 
  92	implements Runnable, ConsoleInterface,Serializable
  93{
  94	/* --- Begin static members --- */
  95
  96	public static final String VERSION = "1.3b2-jedit1";
  97	/*
  98		Debug utils are static so that they are reachable by code that doesn't
  99		necessarily have an interpreter reference (e.g. tracing in utils).
 100		In the future we may want to allow debug/trace to be turned on on
 101		a per interpreter basis, in which case we'll need to use the parent 
 102		reference in some way to determine the scope of the command that 
 103		turns it on or off.
 104	*/
 105    public static boolean DEBUG, TRACE, LOCALSCOPING;
 106
 107	// This should be per instance
 108    transient static PrintStream debug;
 109	static String systemLineSeparator = "\n"; // default
 110
 111	static { 
 112		staticInit();
 113	}
 114
 115	/** Shared system object visible under bsh.system */
 116	static This sharedObject;
 117
 118	/** 
 119		Strict Java mode 
 120		@see setStrictJava( boolean )
 121	*/
 122	private boolean strictJava = false;
 123
 124	/* --- End static members --- */
 125
 126	/* --- Instance data --- */
 127
 128	transient Parser parser;
 129    NameSpace globalNameSpace;
 130    transient Reader in;
 131    transient PrintStream out;
 132    transient PrintStream err;
 133    ConsoleInterface console; 
 134
 135	/** If this interpeter is a child of another, the parent */
 136	Interpreter parent;
 137
 138	/** The name of the file or other source that this interpreter is reading */
 139	String sourceFileInfo;
 140
 141	/** by default in interactive mode System.exit() on EOF */
 142	private boolean exitOnEOF = true;
 143
 144    protected boolean 
 145		evalOnly, 		// Interpreter has no input stream, use eval() only
 146		interactive;	// Interpreter has a user, print prompts, etc.
 147
 148	/* --- End instance data --- */
 149
 150	/**
 151		The main constructor.
 152		All constructors should now pass through here.
 153
 154		@param namespace If namespace is non-null then this interpreter's 
 155		root namespace will be set to the one provided.  If it is null a new 
 156		one will be created for it.
 157		@param parent The parent interpreter if this interpreter is a child 
 158			of another.  May be null.  Children share a BshClassManager with
 159			their parent instance.
 160		@param sourceFileInfo An informative string holding the filename 
 161		or other description of the source from which this interpreter is
 162		reading... used for debugging.  May be null.
 163	*/
 164    public Interpreter(
 165		Reader in, PrintStream out, PrintStream err, 
 166		boolean interactive, NameSpace namespace,
 167		Interpreter parent, String sourceFileInfo )
 168    {
 169		parser = new Parser( in );
 170		long t1=System.currentTimeMillis();
 171        this.in = in;
 172        this.out = out;
 173        this.err = err;
 174        this.interactive = interactive;
 175		debug = err;
 176		this.parent = parent;
 177		if ( parent != null )
 178			setStrictJava( parent.getStrictJava() );
 179		this.sourceFileInfo = sourceFileInfo;
 180
 181		if ( namespace == null )
 182        	this.globalNameSpace = new NameSpace( 
 183				BshClassManager.createClassManager(), "global");
 184		else
 185			this.globalNameSpace = namespace;
 186
 187		// now done in NameSpace automatically when root
 188		// The classes which are imported by default
 189		//globalNameSpace.loadDefaultImports();
 190
 191		/* 
 192			Create the root "bsh" system object if it doesn't exist.
 193		*/
 194		if ( ! ( getu("bsh") instanceof bsh.This ) )
 195			initRootSystemObject();
 196
 197		if ( interactive )
 198			loadRCFiles();
 199
 200		long t2=System.currentTimeMillis();
 201		if ( Interpreter.DEBUG ) 
 202			Interpreter.debug("Time to initialize interpreter: "+(t2-t1));
 203    }
 204
 205    public Interpreter(
 206		Reader in, PrintStream out, PrintStream err, 
 207		boolean interactive, NameSpace namespace)
 208    {
 209		this( in, out, err, interactive, namespace, null, null );
 210	}
 211
 212    public Interpreter(
 213		Reader in, PrintStream out, PrintStream err, boolean interactive)
 214    {
 215        this(in, out, err, interactive, null);
 216    }
 217
 218	/**
 219		Construct a new interactive interpreter attached to the specified 
 220		console using the specified parent namespace.
 221	*/
 222    public Interpreter(ConsoleInterface console, NameSpace globalNameSpace) {
 223
 224        this( console.getIn(), console.getOut(), console.getErr(), 
 225			true, globalNameSpace );
 226
 227		setConsole( console );
 228    }
 229
 230	/**
 231		Construct a new interactive interpreter attached to the specified 
 232		console.
 233	*/
 234    public Interpreter(ConsoleInterface console) {
 235        this(console, null);
 236    }
 237
 238	/**
 239		Create an interpreter for evaluation only.
 240	*/
 241    public Interpreter()
 242    {
 243		this( new StringReader(""), 
 244			System.out, System.err, false, null );
 245        evalOnly = true;
 246		setu( "bsh.evalOnly", new Primitive(true) );
 247    }
 248
 249	// End constructors
 250
 251	/**
 252		Attach a console
 253		Note: this method is incomplete.
 254	*/
 255	public void setConsole( ConsoleInterface console ) {
 256		this.console = console;
 257		setu( "bsh.console", console );
 258		// redundant with constructor
 259		setOut( console.getOut() );
 260		setErr( console.getErr() );
 261		// need to set the input stream - reinit the parser?
 262	}
 263
 264	private void initRootSystemObject() 
 265	{
 266		BshClassManager bcm = getClassManager();
 267//System.out.println("init root bcm ="+bcm);
 268		// bsh
 269		setu("bsh", new NameSpace( bcm, "Bsh Object" ).getThis( this ) );
 270
 271		// init the static shared sharedObject if it's not there yet
 272		if ( sharedObject == null )
 273			sharedObject = new NameSpace( 
 274				bcm, "Bsh Shared System Object" ).getThis( this );
 275		// bsh.system
 276		setu( "bsh.system", sharedObject );
 277		setu( "bsh.shared", sharedObject ); // alias
 278
 279		// bsh.help
 280		This helpText = new NameSpace( 
 281			bcm, "Bsh Command Help Text" ).getThis( this );
 282		setu( "bsh.help", helpText );
 283
 284		// bsh.cwd
 285		try {
 286			setu( "bsh.cwd", System.getProperty("user.dir") );
 287		} catch ( SecurityException e ) { 
 288			// applets can't see sys props
 289			setu( "bsh.cwd", "." );
 290		}
 291
 292		// bsh.interactive
 293		setu( "bsh.interactive", new Primitive(interactive) );
 294		// bsh.evalOnly
 295		setu( "bsh.evalOnly", new Primitive(evalOnly) );
 296	}
 297
 298	/**
 299		Set the global namespace for this interpreter.
 300		<p>
 301
 302		Note: This is here for completeness.  If you're using this a lot 
 303		it may be an indication that you are doing more work than you have 
 304		to.  For example, caching the interpreter instance rather than the 
 305		namespace should not add a significant overhead.  No state other 
 306		than the debug status is stored in the interpreter.
 307		<p>
 308
 309		All features of the namespace can also be accessed using the 
 310		interpreter via eval() and the script variable 'this.namespace'
 311		(or global.namespace as necessary).
 312	*/
 313	public void setNameSpace( NameSpace globalNameSpace ) {
 314		this.globalNameSpace = globalNameSpace;
 315	}
 316
 317	/**
 318		Get the global namespace of this interpreter.
 319		<p>
 320
 321		Note: This is here for completeness.  If you're using this a lot 
 322		it may be an indication that you are doing more work than you have 
 323		to.  For example, caching the interpreter instance rather than the 
 324		namespace should not add a significant overhead.  No state other than 
 325		the debug status is stored in the interpreter.  
 326		<p>
 327
 328		All features of the namespace can also be accessed using the 
 329		interpreter via eval() and the script variable 'this.namespace'
 330		(or global.namespace as necessary).
 331	*/
 332	public NameSpace getNameSpace() {
 333		return globalNameSpace;
 334	}
 335
 336	/**
 337		Run the text only interpreter on the command line or specify a file.
 338	*/
 339    public static void main( String [] args ) 
 340	{
 341        if ( args.length > 0 ) {
 342			String filename = args[0];
 343
 344			String [] bshArgs;
 345			if ( args.length > 1 ) {
 346				bshArgs = new String [ args.length -1 ];
 347				System.arraycopy( args, 1, bshArgs, 0, args.length-1 );
 348			} else
 349				bshArgs = new String [0];
 350
 351            Interpreter interpreter = new Interpreter();
 352			interpreter.setu( "bsh.args", bshArgs );
 353			try {
 354				interpreter.source( filename, interpreter.globalNameSpace );
 355			} catch ( FileNotFoundException e ) {
 356				System.out.println("File not found: "+e);
 357			} catch ( TargetError e ) {
 358				System.out.println("Script threw exception: "+e);
 359				if ( e.inNativeCode() )
 360					e.printStackTrace( DEBUG, System.err );
 361			} catch ( EvalError e ) {
 362				System.out.println("Evaluation Error: "+e);
 363			} catch ( IOException e ) {
 364				System.out.println("I/O Error: "+e);
 365			}
 366        } else {
 367			// Workaround for JDK bug 4071281, where system.in.available() 
 368			// returns too large a value. This bug has been fixed in JDK 1.2.
 369			InputStream src;
 370			if ( System.getProperty("os.name").startsWith("Windows") 
 371				&& System.getProperty("java.version").startsWith("1.1."))
 372			{
 373				src = new FilterInputStream(System.in) {
 374					public int available() throws IOException {
 375						return 0;
 376					}
 377				};
 378			}
 379			else
 380				src = System.in;
 381
 382            Reader in = new CommandLineReader( new InputStreamReader(src));
 383            Interpreter interpreter = 
 384				new Interpreter( in, System.out, System.err, true );
 385        	interpreter.run();
 386        }
 387    }
 388
 389	/**
 390		Run interactively.  (printing prompts, etc.)
 391	*/
 392    public void run() 
 393	{
 394        if(evalOnly)
 395            throw new RuntimeException("bsh Interpreter: No stream");
 396
 397        /*
 398          We'll print our banner using eval(String) in order to
 399          exercise the parser and get the basic expression classes loaded...
 400          This ameliorates the delay after typing the first statement.
 401        */
 402        if ( interactive )
 403			try { 
 404				eval("printBanner();"); 
 405			} catch ( EvalError e ) {
 406				println(
 407					"BeanShell "+VERSION+" - by Pat Niemeyer (pat@pat.net)");
 408			}
 409
 410		// init the callstack.  
 411		CallStack callstack = new CallStack( globalNameSpace );
 412
 413        boolean eof = false;
 414        while( !eof )
 415        {
 416            try
 417            {
 418                // try to sync up the console
 419                System.out.flush();
 420                System.err.flush();
 421                Thread.yield();  // this helps a little
 422
 423                if ( interactive )
 424                    print( getBshPrompt() );
 425
 426                eof = Line();
 427
 428                if( get_jjtree().nodeArity() > 0 )  // number of child nodes 
 429                {
 430                    SimpleNode node = (SimpleNode)(get_jjtree().rootNode());
 431
 432                    if(DEBUG)
 433                        node.dump(">");
 434
 435                    Object ret = node.eval( callstack, this );
 436				
 437					// sanity check during development
 438					if ( callstack.depth() > 1 )
 439						throw new InterpreterError(
 440							"Callstack growing: "+callstack);
 441
 442                    if(ret instanceof ReturnControl)
 443                        ret = ((ReturnControl)ret).value;
 444                    if(ret != Primitive.VOID)
 445                    {
 446                        setu("$_", ret);
 447                        Object show = getu("bsh.show");
 448                        if(show instanceof Boolean &&
 449                            ((Boolean)show).booleanValue() == true)
 450                            println("<" + ret + ">");
 451                    }
 452                }
 453            }
 454            catch(ParseException e)
 455            {
 456                error("Parser Error: " + e.getMessage(DEBUG));
 457				if ( DEBUG )
 458                	e.printStackTrace();
 459                if(!interactive)
 460                    eof = true;
 461
 462                parser.reInitInput(in);
 463            }
 464            catch(InterpreterError e)
 465            {
 466                error("Internal Error: " + e.getMessage());
 467                e.printStackTrace();
 468                if(!interactive)
 469                    eof = true;
 470            }
 471            catch(TargetError e)
 472            {
 473                error("// Uncaught Exception: " + e );
 474				if ( e.inNativeCode() )
 475					e.printStackTrace( DEBUG, err );
 476                if(!interactive)
 477                    eof = true;
 478				setu("$_e", e.getTarget());
 479            }
 480            catch (EvalError e)
 481            {
 482				if ( interactive )
 483					error( "EvalError: "+e.toString() );
 484				else
 485					error( "EvalError: "+e.getMessage() );
 486
 487                if(DEBUG)
 488                    e.printStackTrace();
 489
 490                if(!interactive)
 491                    eof = true;
 492            }
 493            catch(Exception e)
 494            {
 495                error("Unknown error: " + e);
 496				if ( DEBUG )
 497                	e.printStackTrace();
 498                if(!interactive)
 499                    eof = true;
 500            }
 501            catch(TokenMgrError e)
 502            {
 503				error("Error parsing input: " + e);
 504
 505				/*
 506					We get stuck in infinite loops here when unicode escapes
 507					fail.  Must re-init the char stream reader 
 508					(ASCII_UCodeESC_CharStream.java)
 509				*/
 510				parser.reInitTokenInput( in );
 511
 512                if(!interactive)
 513                    eof = true;
 514            }
 515            finally
 516            {
 517                get_jjtree().reset();
 518				// reinit the callstack
 519				if ( callstack.depth() > 1 ) {
 520					callstack.clear();
 521					callstack.push( globalNameSpace );
 522				}
 523            }
 524        }
 525
 526		if ( interactive && exitOnEOF )
 527			System.exit(0);
 528    }
 529
 530	// begin source and eval
 531
 532	/**
 533		Read text from fileName and eval it.
 534	*/
 535    public Object source( String filename, NameSpace nameSpace ) 
 536		throws FileNotFoundException, IOException, EvalError 
 537	{
 538		File file = pathToFile( filename );
 539		if ( Interpreter.DEBUG ) debug("Sourcing file: "+file);
 540		Reader sourceIn = new BufferedReader( new FileReader(file) );
 541		try {
 542			return eval( sourceIn, nameSpace, filename );
 543		} finally {
 544			sourceIn.close();
 545		}
 546	}
 547
 548	/**
 549		Read text from fileName and eval it.
 550		Convenience method.  Use the global namespace.
 551	*/
 552    public Object source( String filename ) 
 553		throws FileNotFoundException, IOException, EvalError 
 554	{
 555		return source( filename, globalNameSpace );
 556	}
 557
 558    /**
 559        Spawn a non-interactive local interpreter to evaluate text in the 
 560		specified namespace.  
 561
 562		Return value is the evaluated object (or corresponding primitive 
 563		wrapper).
 564
 565		@param sourceFileInfo is for information purposes only.  It is used to
 566		display error messages (and in the future may be made available to
 567		the script).
 568		@throws EvalError on script problems
 569		@throws TargetError on unhandled exceptions from the script
 570    */
 571	/*
 572		Note: we need a form of eval that passes the callstack through...
 573	*/
 574	/*
 575	Can't this be combined with run() ?
 576	run seems to have stuff in it for interactive vs. non-interactive...
 577	compare them side by side and see what they do differently, aside from the
 578	exception handling.
 579	*/
 580
 581    public Object eval( 
 582		Reader in, NameSpace nameSpace, String sourceFileInfo
 583			/*, CallStack callstack */ ) 
 584		throws EvalError 
 585	{
 586		Object retVal = null;
 587		if ( Interpreter.DEBUG ) debug("eval: nameSpace = "+nameSpace);
 588
 589		/* 
 590			Create non-interactive local interpreter for this namespace
 591			with source from the input stream and out/err same as 
 592			this interpreter.
 593		*/
 594        Interpreter localInterpreter = 
 595			new Interpreter( 
 596				in, out, err, false, nameSpace, this, sourceFileInfo  );
 597
 598		CallStack callstack = new CallStack( nameSpace );
 599
 600        boolean eof = false;
 601        while(!eof)
 602        {
 603			SimpleNode node = null;
 604            try
 605            {
 606                eof = localInterpreter.Line();
 607                if (localInterpreter.get_jjtree().nodeArity() > 0)
 608                {
 609                    node = (SimpleNode)localInterpreter.get_jjtree().rootNode();
 610					// nodes remember from where they were sourced
 611					node.setSourceFile( sourceFileInfo );
 612
 613					if ( TRACE )
 614						println( "// " +node.getText() );
 615
 616                    retVal = node.eval( callstack, localInterpreter );
 617
 618					// sanity check during development
 619					if ( callstack.depth() > 1 )
 620						throw new InterpreterError(
 621							"Callstack growing: "+callstack);
 622
 623                    if ( retVal instanceof ReturnControl ) {
 624                        retVal = ((ReturnControl)retVal).value;
 625						break; // non-interactive, return control now
 626					}
 627                }
 628            } catch(ParseException e) {
 629				/*
 630                throw new EvalError(
 631					"Sourced file: "+sourceFileInfo+" parser Error: " 
 632					+ e.getMessage( DEBUG ), node, callstack );
 633				*/
 634				if ( DEBUG )
 635					// show extra "expecting..." info
 636					error( e.getMessage(DEBUG) );
 637
 638				// add the source file info and throw again
 639				e.setErrorSourceFile( sourceFileInfo );
 640				throw e;
 641
 642            } catch ( InterpreterError e ) {
 643                e.printStackTrace();
 644                throw new EvalError(
 645					"Sourced file: "+sourceFileInfo+" internal Error: " 
 646					+ e.getMessage(), node, callstack);
 647            } catch ( TargetError e ) {
 648				// failsafe, set the Line as the origin of the error.
 649				if ( e.getNode()==null )
 650					e.setNode( node );
 651				e.reThrow("Sourced file: "+sourceFileInfo);
 652            } catch ( EvalError e) {
 653                if ( DEBUG)
 654                    e.printStackTrace();
 655				// failsafe, set the Line as the origin of the error.
 656				if ( e.getNode()==null )
 657					e.setNode( node );
 658				e.reThrow( "Sourced file: "+sourceFileInfo );
 659            } catch ( Exception e) {
 660                if ( DEBUG)
 661                	e.printStackTrace();
 662                throw new EvalError(
 663					"Sourced file: "+sourceFileInfo+" unknown error: " 
 664					+ e.getMessage(), node, callstack);
 665            } catch(TokenMgrError e) {
 666                throw new EvalError(
 667					"Sourced file: "+sourceFileInfo+" Token Parsing Error: " 
 668					+ e.getMessage(), node, callstack );
 669            } finally {
 670                localInterpreter.get_jjtree().reset();
 671
 672				// reinit the callstack
 673				if ( callstack.depth() > 1 ) {
 674					callstack.clear();
 675					callstack.push( nameSpace );
 676				}
 677            }
 678        }
 679		return Primitive.unwrap( retVal );
 680    }
 681
 682	/**
 683		Evaluate the inputstream in this interpreter's global namespace.
 684	*/
 685    public Object eval( Reader in ) throws EvalError 
 686	{
 687		return eval( in, globalNameSpace, "eval stream" );
 688	}
 689
 690	/**
 691		Evaluate the string in this interpreter's global namespace.
 692	*/
 693    public Object eval( String statements ) throws EvalError {
 694		if ( Interpreter.DEBUG ) debug("eval(String): "+statements);
 695		return eval(statements, globalNameSpace);
 696	}
 697
 698	/**
 699		Evaluate the string in the specified namespace.
 700	*/
 701    public Object eval( String statements, NameSpace nameSpace ) 
 702		throws EvalError 
 703	{
 704
 705		String s = ( statements.endsWith(";") ? statements : statements+";" );
 706        return eval( 
 707			new StringReader(s), nameSpace, 
 708			"inline evaluation of: ``"+ showEvalString(s)+"''" );
 709    }
 710
 711	private String showEvalString( String s ) {
 712		s = s.replace('\n', ' ');
 713		s = s.replace('\r', ' ');
 714		if ( s.length() > 80 )
 715			s = s.substring( 0, 80 ) + " . . . ";
 716		return s;
 717	}
 718
 719	// end source and eval
 720
 721	/**
 722		Print an error message in a standard format on the output stream
 723		associated with this interpreter. On the GUI console this will appear 
 724		in red, etc.
 725	*/
 726    public final void error(String s) {
 727		if ( console != null )
 728				console.error( "// Error: " + s +"\n" );
 729		else {
 730			err.println("// Error: " + s);
 731			err.flush();
 732		}
 733    }
 734
 735	// ConsoleInterface
 736	// The interpreter reflexively implements the console interface that it 
 737	// uses.  Should clean this up by using an inner class to implement the
 738	// console for us.
 739
 740	/** 
 741		Get the input stream associated with this interpreter.
 742		This may be be stdin or the GUI console.
 743	*/
 744	public Reader getIn() { return in; }
 745
 746	/** 
 747		Get the outptut stream associated with this interpreter.
 748		This may be be stdout or the GUI console.
 749	*/
 750	public PrintStream getOut() { return out; }
 751
 752	/** 
 753		Get the error output stream associated with this interpreter.
 754		This may be be stderr or the GUI console.
 755	*/
 756	public PrintStream getErr() { return err; }
 757
 758    public final void println(String s)
 759    {
 760        //print(s + "\n");
 761        print( s + systemLineSeparator );
 762    }
 763
 764    public final void print(String s)
 765    {
 766		if (console != null) {
 767            console.print(s);
 768        } else {
 769            out.print(s);
 770            out.flush();
 771        }
 772    }
 773
 774	// End ConsoleInterface
 775
 776	/**
 777		Print a debug message on debug stream associated with this interpreter
 778		only if debugging is turned on.
 779	*/
 780    public final static void debug(String s)
 781    {
 782        if ( DEBUG )
 783            debug.println("// Debug: " + s);
 784    }
 785
 786	/* 
 787		Primary interpreter set and get variable methods
 788		Note: These are squeltching errors... should they?
 789	*/
 790
 791	/**
 792		Get the value of the name.
 793		name may be any value. e.g. a variable or field
 794	*/
 795    public Object get( String name ) throws EvalError {
 796		try {
 797			Object ret = globalNameSpace.get( name, this );
 798			return Primitive.unwrap( ret );
 799		} catch ( UtilEvalError e ) { 
 800			throw e.toEvalError( SimpleNode.JAVACODE, new CallStack() ); 
 801		}
 802	}
 803
 804	/**
 805		Unchecked get for internal use
 806	*/
 807    Object getu( String name ) {
 808		try { 
 809			return get( name );
 810		} catch ( EvalError e ) { 
 811			throw new InterpreterError("set: "+e);
 812		}
 813	}
 814
 815	/**
 816		Assign the value to the name.	
 817		name may evaluate to anything assignable. e.g. a variable or field.
 818	*/
 819    public void set( String name, Object value )
 820		throws EvalError 
 821	{
 822		// map null to Primtive.NULL coming in...
 823		if ( value == null )
 824			value = Primitive.NULL;
 825
 826		CallStack callstack = new CallStack();
 827		try {
 828			if ( Name.isCompound( name ) ) 
 829			{
 830				LHS lhs = globalNameSpace.getNameResolver( name ).toLHS( 
 831					callstack, this );
 832				lhs.assign( value, false );
 833			} else // optimization for common case
 834				globalNameSpace.setVariable( name, value, false );
 835		} catch ( UtilEvalError e ) { 
 836			throw e.toEvalError( SimpleNode.JAVACODE, callstack ); 
 837		}
 838	}
 839
 840	/**
 841		Unchecked set for internal use
 842	*/
 843    void setu(String name, Object value) {
 844		try { 
 845			set(name, value);
 846		} catch ( EvalError e ) { 
 847			throw new InterpreterError("set: "+e);
 848		}
 849	}
 850
 851    public void set(String name, long value) throws EvalError {
 852        set(name, new Primitive(value));
 853	}
 854    public void set(String name, int value) throws EvalError {
 855        set(name, new Primitive(value));
 856	}
 857    public void set(String name, double value) throws EvalError {
 858        set(name, new Primitive(value));
 859	}
 860    public void set(String name, float value) throws EvalError {
 861        set(name, new Primitive(value));
 862	}
 863    public void set(String name, boolean value) throws EvalError {
 864        set(name, new Primitive(value));
 865	}
 866
 867	/**
 868		Unassign the variable name.	
 869		Name should evaluate to a variable.
 870	*/
 871    public void unset( String name ) 
 872		throws EvalError 
 873	{
 874		/*
 875			We jump through some hoops here to handle arbitrary cases like
 876			unset("bsh.foo");
 877		*/
 878		CallStack callstack = new CallStack();
 879		try {
 880			LHS lhs = globalNameSpace.getNameResolver( name ).toLHS( 
 881				callstack, this );
 882
 883			if ( lhs.type != LHS.VARIABLE )
 884				throw new EvalError("Can't unset, not a variable: "+name, 
 885					SimpleNode.JAVACODE, new CallStack() );
 886
 887			//lhs.assign( null, false );
 888			lhs.nameSpace.unsetVariable( name );
 889		} catch ( UtilEvalError e ) {
 890			throw new EvalError( e.getMessage(), 
 891				SimpleNode.JAVACODE, new CallStack() );
 892		}
 893	}
 894
 895	// end primary set and get methods
 896
 897	/**
 898		Get a reference to the interpreter (global namespace), cast 
 899		to the specified interface type.  Assuming the appropriate 
 900		methods of the interface are defined in the interpreter, then you may 
 901		use this interface from Java, just like any other Java object.
 902		<p>
 903
 904		For example:
 905		<pre>
 906			Interpreter interpreter = new Interpreter();
 907			// define a method called run()
 908			interpreter.eval("run() { ... }");
 909		
 910			// Fetch a reference to the interpreter as a Runnable
 911			Runnable runnable = 
 912				(Runnable)interpreter.getInterface( Runnable.class );
 913		</pre>
 914		<p>
 915
 916		Note that the interpreter does *not* require that any or all of the
 917		methods of the interface be defined at the time the interface is
 918		generated.  However if you attempt to invoke one that is not defined
 919		you will get a runtime exception.
 920		<p>
 921
 922		Note also that this convenience method has exactly the same effect as 
 923		evaluating the script:
 924		<pre>
 925			(Type)this;
 926		</pre>
 927		<p>
 928
 929		For example, the following is identical to the previous example:
 930		<p>
 931
 932		<pre>
 933			// Fetch a reference to the interpreter as a Runnable
 934			Runnable runnable = 
 935				(Runnable)interpreter.eval( "(Runnable)this" );
 936		</pre>
 937		<p>
 938
 939		<em>Version requirement</em> Although standard Java interface types 
 940		are always available, to be used with arbitrary interfaces this 
 941		feature requires that you are using Java 1.3 or greater.
 942		<p>
 943
 944		@throws EvalError if the interface cannot be generated because the
 945		version of Java does not support the proxy mechanism. 
 946	*/
 947	public Object getInterface( Class interf ) throws EvalError
 948	{
 949		try {
 950			return globalNameSpace.getThis( this ).getInterface( interf );
 951		} catch ( UtilEvalError e ) {
 952			throw e.toEvalError( SimpleNode.JAVACODE, new CallStack() );
 953		}
 954	}
 955
 956	/*	Methods for interacting with Parser */
 957
 958	private JJTParserState get_jjtree() {
 959		return parser.jjtree;
 960	}
 961
 962  	private JavaCharStream get_jj_input_stream() {
 963		return parser.jj_input_stream;
 964	}
 965
 966  	private boolean Line() throws ParseException {
 967		return parser.Line();
 968	}
 969
 970	/*	End methods for interacting with Parser */
 971
 972	void loadRCFiles() {
 973		try {
 974			String rcfile = 
 975				// Default is c:\windows under win98, $HOME under Unix
 976				System.getProperty("user.home") + File.separator + ".bshrc";
 977			source( rcfile, globalNameSpace );
 978		} catch ( Exception e ) { 
 979			// squeltch security exception, filenotfoundexception
 980			if ( Interpreter.DEBUG ) debug("Could not find rc file: "+e);
 981		}
 982	}
 983
 984	/**
 985		Localize a path to the file name based on the bsh.cwd interpreter 
 986		working directory.
 987	*/
 988    public File pathToFile( String fileName ) 
 989		throws IOException
 990	{
 991		File file = new File( fileName );
 992
 993		// if relative, fix up to bsh.cwd
 994		if ( !file.isAbsolute() ) {
 995			String cwd = (String)getu("bsh.cwd");
 996			file = new File( cwd + File.separator + fileName );
 997		}
 998
 999		// The canonical file name is also absolute.
1000		// No need for getAbsolutePath() here...
1001		return new File( file.getCanonicalPath() );
1002	}
1003
1004	public static void redirectOutputToFile( String filename ) 
1005	{
1006		try {
1007			PrintStream pout = new PrintStream( 
1008				new FileOutputStream( filename ) );
1009			System.setOut( pout );
1010			System.setErr( pout );
1011		} catch ( IOException e ) {
1012			System.err.println("Can't redirect output to file: "+filename );
1013		}
1014	}
1015
1016	/**
1017		Set an external class loader to be used for all basic class loading
1018		in BeanShell.  
1019		<p>
1020
1021		BeanShell will use this at the same point it would otherwise use the 
1022		plain Class.forName().
1023		i.e. if no explicit classpath management is done from the script
1024		(addClassPath(), setClassPath(), reloadClasses()) then BeanShell will
1025		only use the supplied classloader.  If additional classpath management
1026		is done then BeanShell will perform that in addition to the supplied
1027		external classloader.  
1028		However BeanShell is not currently able to reload
1029		classes supplied through the external classloader.
1030		<p>
1031
1032		@see BshClassManager#setClassLoader( ClassLoader )
1033	*/
1034	public void setClassLoader( ClassLoader externalCL ) {
1035		getClassManager().setClassLoader( externalCL );
1036	}
1037
1038	/**
1039		Get the class manager associated with this interpreter
1040		(the BshClassManager of this interpreter's global namespace).
1041		This is primarily a convenience method.
1042	*/
1043	public BshClassManager getClassManager() 
1044	{
1045		return getNameSpace().getClassManager();
1046	}
1047	
1048	/**
1049		Set the class manager.
1050	public setClassManager( BshClassManager manager ) 
1051	{
1052		this.classManager = manager;
1053	}
1054	*/
1055
1056	/**
1057		Set strict Java mode on or off.  
1058		This mode attempts to make BeanShell syntax behave as Java
1059		syntax, eliminating conveniences like loose variables, etc.
1060		When enabled, variables are required to be declared or initialized 
1061		before use and method arguments are reqired to have types. 
1062		<p>
1063
1064		This mode will become more strict in a future release when 
1065		classes are interpreted and there is an alternative to scripting
1066		objects as method closures.
1067	*/
1068	public void setStrictJava( boolean b ) { 
1069		this.strictJava = b; 
1070	}
1071
1072	/**
1073		@see #setStrictJava( boolean )
1074	*/
1075	public boolean getStrictJava() { 
1076		return this.strictJava;
1077	}
1078
1079	static void staticInit() 
1080	{
1081	/* 
1082		Apparently in some environments you can't catch the security exception
1083		at all...  e.g. as an applet in IE  ... will probably have to work 
1084		around 
1085	*/
1086		try {
1087			systemLineSeparator = System.getProperty("line.separator");
1088    		debug = System.err;
1089    		DEBUG = Boolean.getBoolean("debug");
1090    		TRACE = Boolean.getBoolean("trace");
1091    		LOCALSCOPING = Boolean.getBoolean("localscoping");
1092			String outfilename = System.getProperty("outfile");
1093			if ( outfilename != null )
1094				redirectOutputToFile( outfilename );
1095		} catch ( SecurityException e ) { 
1096			System.err.println("Could not init static:"+e);
1097		} catch ( Exception e ) {
1098			System.err.println("Could not init static(2):"+e);
1099		} catch ( Throwable e ) { 
1100			System.err.println("Could not init static(3):"+e);
1101		}
1102	}
1103
1104	/**
1105		Specify the source of the text from which this interpreter is reading.
1106		Note: there is a difference between what file the interrpeter is 
1107		sourcing and from what file a method was originally parsed.  One
1108		file may call a method sourced from another file.  See SimpleNode
1109		for origination file info.
1110		@see bsh.SimpleNode#getSourceFile()
1111	*/
1112	public String getSourceFileInfo() { 
1113		if ( sourceFileInfo != null )
1114			return sourceFileInfo;
1115		else
1116			return "<unknown source>";
1117	}
1118
1119	/**
1120		Get the parent Interpreter of this interpreter, if any.
1121		Currently this relationship implies the following:
1122			1) Parent and child share a BshClassManager
1123			2) Children indicate the parent's source file information in error
1124			reporting.
1125		When created as part of a source() / eval() the child also shares
1126		the parent's namespace.  But that is not necessary in general.
1127	*/
1128	public Interpreter getParent() {
1129		return parent;
1130	}
1131
1132	public void setOut( PrintStream out ) {
1133		this.out = out;
1134	}
1135	public void setErr( PrintStream err ) {
1136		this.err = err;
1137	}
1138
1139	/**
1140		De-serialization setup.
1141		Default out and err streams to stdout, stderr if they are null.
1142	*/
1143	private void readObject(ObjectInputStream stream) 
1144		throws java.io.IOException, ClassNotFoundException
1145	{
1146		stream.defaultReadObject();
1147
1148		// set transient fields
1149		if ( console != null ) {
1150			setOut( console.getOut() );
1151			setErr( console.getErr() );
1152		} else {
1153			setOut( System.out );
1154			setErr( System.err );
1155		}
1156	}
1157
1158	/**
1159		Get the prompt string defined by the getBshPrompt() method in the
1160		global namespace.  This may be from the getBshPrompt() command or may
1161		be defined by the user as with any other method.
1162		Defaults to "bsh % " if the method is not defined or there is an error.
1163	*/
1164	private String getBshPrompt() 
1165	{
1166		try {
1167			return (String)eval("getBshPrompt()");
1168		} catch ( Exception e ) {
1169			return "bsh % ";
1170		}
1171	}
1172
1173	/**
1174		Specify whether, in interactive mode, the interpreter exits Java upon
1175		end of input.  If true, when in interactive mode the interpreter will
1176		issue a System.exit(0) upon eof.  If false the interpreter no
1177		System.exit() will be done.
1178		<p/>
1179		Note: if you wish to cause an EOF externally you can try closing the
1180		input stream.  This is not guaranteed to work in older versions of Java
1181		due to Java limitations, but should work in newer JDK/JREs.  (That was
1182		the motivation for the Java NIO package).
1183	*/
1184	public void setExitOnEOF( boolean value ) {
1185		exitOnEOF = value; // ug
1186	}
1187}
1188