wasCSharpSQLite – Rev 1

Subversion Repositories:
Rev:
#undef DEBUG
/*
* Interp.java --
*
*       Implements the core Tcl interpreter.
*
* Copyright (c) 1997 Cornell University.
* Copyright (c) 1997-1998 Sun Microsystems, Inc.
*
* See the file "license.terms" for information on usage and
* redistribution of this file, and for a DISCLAIMER OF ALL
* WARRANTIES.
* 
* Included in SQLite3 port to C# for use in testharness only;  2008 Noah B Hart
*
* RCS @(#) $Id: Interp.java,v 1.44 2003/07/25 16:38:35 mdejong Exp $
*
*/
using System;
using System.Collections;
using System.IO;
using System.Text;

namespace tcl.lang
{

  /// <summary> The Tcl interpreter class.</summary>

  public class Interp : EventuallyFreed
  {
    private void InitBlock()
    {
      reflectObjTable = new Hashtable();
      reflectConflictTable = new Hashtable();
      importTable = new Hashtable[] { new Hashtable(), new Hashtable() };
    }
    /// <summary> Returns the name of the script file currently under execution.
    /// 
    /// </summary>
    /// <returns> the name of the script file currently under execution.
    /// </returns>
    internal string ScriptFile
    {
      get
      {
        return dbg.fileName;
      }

    }

    // The following three variables are used to maintain a translation
    // table between ReflectObject's and their string names. These
    // variables are accessed by the ReflectObject class, they
    // are defined here be cause we need them to be per interp data.

    // Translates Object to ReflectObject. This makes sure we have only
    // one ReflectObject internalRep for the same Object -- this
    // way Object identity can be done by string comparison.

    internal Hashtable reflectObjTable;

    // Number of reflect objects created so far inside this Interp
    // (including those that have be freed)

    internal long reflectObjCount = 0;

    // Table used to store reflect hash index conflicts, see
    // ReflectObject implementation for more details

    internal Hashtable reflectConflictTable;

    // The number of chars to copy from an offending command into error
    // message.

    private const int MAX_ERR_LENGTH = 200;


    // We pretend this is Tcl 8.0, patch level 0.

    internal const string TCL_VERSION = "8.0";
    internal const string TCL_PATCH_LEVEL = "8.0";


    // Total number of times a command procedure
    // has been called for this interpreter.

    protected internal int cmdCount;

    // FIXME : remove later
    // Table of commands for this interpreter.
    //Hashtable cmdTable;

    // Table of channels currently registered in this interp.

    internal Hashtable interpChanTable;

    // The Notifier associated with this Interp.

    private Notifier notifier;

    // Hash table for associating data with this interpreter. Cleaned up
    // when this interpreter is deleted.

    internal Hashtable assocData;

    // Current working directory.

    private FileInfo workingDir;

    // Points to top-most in stack of all nested procedure
    // invocations.  null means there are no active procedures.

    internal CallFrame frame;

    // Points to the call frame whose variables are currently in use
    // (same as frame unless an "uplevel" command is being
    // executed).  null means no procedure is active or "uplevel 0" is
    // being exec'ed.

    internal CallFrame varFrame;

    // The interpreter's global namespace.

    internal NamespaceCmd.Namespace globalNs;

    // Hash table used to keep track of hidden commands on a per-interp basis.

    internal Hashtable hiddenCmdTable;

    // Information used by InterpCmd.java to keep
    // track of master/slave interps on a per-interp basis.

    // Keeps track of all interps for which this interp is the Master.
    // First, slaveTable (a hashtable) maps from names of commands to
    // slave interpreters. This hashtable is used to store information
    // about slave interpreters of this interpreter, to map over all slaves, etc.

    internal Hashtable slaveTable;

    // Hash table for Target Records. Contains all Target records which denote
    // aliases from slaves or sibling interpreters that direct to commands in
    // this interpreter. This table is used to remove dangling pointers
    // from the slave (or sibling) interpreters when this interpreter is deleted.

    internal Hashtable targetTable;

    // Information necessary for this interp to function as a slave.
    internal InterpSlaveCmd slave;

    // Table which maps from names of commands in slave interpreter to
    // InterpAliasCmd objects.

    internal Hashtable aliasTable;

    // FIXME : does globalFrame need to be replaced by globalNs?
    // Points to the global variable frame.

    //CallFrame globalFrame;

    // The script file currently under execution. Can be null if the
    // interpreter is not evaluating any script file.

    internal string scriptFile;

    // Number of times the interp.eval() routine has been recursively
    // invoked.

    internal int nestLevel;

    // Used to catch infinite loops in Parser.eval2.

    internal int maxNestingDepth;

    // Flags used when evaluating a command.

    internal int evalFlags;

    // Flags used when evaluating a command.

    public int flags;

    // Is this interpreted marked as safe?

    internal bool isSafe;

    // Offset of character just after last one compiled or executed
    // by Parser.eval2().

    internal int termOffset;

    // List of name resolution schemes added to this interpreter.
    // Schemes are added/removed by calling addInterpResolver and
    // removeInterpResolver.

    internal ArrayList resolvers;

    // The expression parser for this interp.

    internal Expression expr;

    // Used by the Expression class.  If it is equal to zero, then the 
    // parser will evaluate commands and retrieve variable values from 
    // the interp.

    internal int noEval;

    // Used in the Expression.java file for the 
    // SrandFunction.class and RandFunction.class.
    // Set to true if a seed has been set.

    internal bool randSeedInit;

    // Used in the Expression.java file for the SrandFunction.class and
    // RandFunction.class.  Stores the value of the seed.

    internal long randSeed;

    // If returnCode is TCL.CompletionCode.ERROR, stores the errorInfo.

    internal string errorInfo;

    // If returnCode is TCL.CompletionCode.ERROR, stores the errorCode.

    internal string errorCode;

    // Completion code to return if current procedure exits with a
    // TCL_RETURN code.

    protected internal TCL.CompletionCode returnCode;

    // True means the interpreter has been deleted: don't process any
    // more commands for it, and destroy the structure as soon as all
    // nested invocations of eval() are done.

    protected internal bool deleted;

    // True means an error unwind is already in progress. False
    // means a command proc has been invoked since last error occurred.

    protected internal bool errInProgress;

    // True means information has already been logged in $errorInfo
    // for the current eval() instance, so eval() needn't log it
    // (used to implement the "error" command).

    protected internal bool errAlreadyLogged;

    // True means that addErrorInfo has been called to record
    // information for the current error. False means Interp.eval
    // must clear the errorCode variable if an error is returned.

    protected internal bool errCodeSet;

    // When TCL_ERROR is returned, this gives the line number within
    // the command where the error occurred (1 means first line).


    internal int errorLine;

    // Stores the current result in the interpreter.

    private TclObject m_result;

    // Value m_result is set to when resetResult() is called.

    private TclObject m_nullResult;

    // Used ONLY by PackageCmd.

    internal Hashtable packageTable;
    internal string packageUnknown;


    // Used ONLY by the Parser.

    internal TclObject[][][] parserObjv;
    internal int[] parserObjvUsed;

    internal TclToken[] parserTokens;
    internal int parserTokensUsed;


    // Used ONLY by JavaImportCmd
    internal Hashtable[] importTable;

    // List of unsafe commands:

    internal static readonly string[] unsafeCmds = new string[] { "encoding", "exit", "load", "cd", "fconfigure", "file", "glob", "open", "pwd", "socket", "beep", "echo", "ls", "resource", "source", "exec", "source" };

    // Flags controlling the call of invoke.

    internal const int INVOKE_HIDDEN = 1;
    internal const int INVOKE_NO_UNKNOWN = 2;
    internal const int INVOKE_NO_TRACEBACK = 4;

    public Interp()
    {
      InitBlock();

      //freeProc         = null;
      errorLine = 0;

      // An empty result is used pretty often. We will use a shared
      // TclObject instance to represent the empty result so that we
      // don't need to create a new TclObject instance every time the
      // interpreter result is set to empty.

      m_nullResult = TclString.newInstance( "" );
      m_nullResult.preserve(); // Increment refCount to 1
      m_nullResult.preserve(); // Increment refCount to 2 (shared)
      m_result = TclString.newInstance( "" ); //m_nullResult; // correcponds to iPtr->objResultPtr
      m_result.preserve();

      expr = new Expression();
      nestLevel = 0;
      maxNestingDepth = 1000;

      frame = null;
      varFrame = null;

      returnCode = TCL.CompletionCode.OK;
      errorInfo = null;
      errorCode = null;

      packageTable = new Hashtable();
      packageUnknown = null;
      cmdCount = 0;
      termOffset = 0;
      resolvers = null;
      evalFlags = 0;
      scriptFile = null;
      flags = 0;
      isSafe = false;
      assocData = null;


      globalNs = null; // force creation of global ns below
      globalNs = NamespaceCmd.createNamespace( this, null, null );
      if ( globalNs == null )
      {
        throw new TclRuntimeError( "Interp(): can't create global namespace" );
      }


      // Init things that are specific to the Jacl implementation

      workingDir = new FileInfo( System.Environment.CurrentDirectory );
      noEval = 0;

      notifier = Notifier.getNotifierForThread( System.Threading.Thread.CurrentThread );
      notifier.preserve();

      randSeedInit = false;

      deleted = false;
      errInProgress = false;
      errAlreadyLogged = false;
      errCodeSet = false;

      dbg = initDebugInfo();

      slaveTable = new Hashtable();
      targetTable = new Hashtable();
      aliasTable = new Hashtable();

      // init parser variables
      Parser.init( this );
      TclParse.init( this );

      // Initialize the Global (static) channel table and the local
      // interp channel table.

      interpChanTable = TclIO.getInterpChanTable( this );

      // Sets up the variable trace for tcl_precision.

      Util.setupPrecisionTrace( this );

      // Create the built-in commands.

      createCommands();

      try
      {
        // Set up tcl_platform, tcl_version, tcl_library and other
        // global variables.

        setVar( "tcl_platform", "platform", "windows", TCL.VarFlag.GLOBAL_ONLY );
        setVar( "tcl_platform", "byteOrder", "bigEndian", TCL.VarFlag.GLOBAL_ONLY );

        setVar( "tcl_platform", "os", Environment.OSVersion.Platform.ToString(), TCL.VarFlag.GLOBAL_ONLY );
        setVar( "tcl_platform", "osVersion", Environment.OSVersion.Version.ToString(), TCL.VarFlag.GLOBAL_ONLY );
        setVar( "tcl_platform", "machine", Util.tryGetSystemProperty( "os.arch", "?" ), TCL.VarFlag.GLOBAL_ONLY );

        setVar( "tcl_version", TCL_VERSION, TCL.VarFlag.GLOBAL_ONLY );
        setVar( "tcl_patchLevel", TCL_PATCH_LEVEL, TCL.VarFlag.GLOBAL_ONLY );
        setVar( "tcl_library", "resource:/tcl/lang/library", TCL.VarFlag.GLOBAL_ONLY );
        if ( Util.Windows )
        {
          setVar( "tcl_platform", "host_platform", "windows", TCL.VarFlag.GLOBAL_ONLY );
        }
        else if ( Util.Mac )
        {
          setVar( "tcl_platform", "host_platform", "macintosh", TCL.VarFlag.GLOBAL_ONLY );
        }
        else
        {
          setVar( "tcl_platform", "host_platform", "unix", TCL.VarFlag.GLOBAL_ONLY );
        }

        // Create the env array an populated it with proper
        // values.

        Env.initialize( this );

        // Register Tcl's version number. Note: This MUST be 
        // done before the call to evalResource, otherwise
        // calls to "package require tcl" will fail.

        pkgProvide( "Tcl", TCL_VERSION );

        // Source the init.tcl script to initialize auto-loading.

        evalResource( "/tcl/lang/library/init.tcl" );
      }
      catch ( TclException e )
      {
        System.Diagnostics.Debug.WriteLine( getResult().ToString() );
        SupportClass.WriteStackTrace( e, Console.Error );
        throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
      }
    }
    public override void eventuallyDispose()
    {
      if ( deleted )
      {
        return;
      }

      deleted = true;

      if ( nestLevel > 0 )
      {
        //-- TODO -- Determine why this is an error             throw new TclRuntimeError("dispose() called with active evals");
      }

      // Remove our association with the notifer (if we had one).

      if ( notifier != null )
      {
        notifier.release();
        notifier = null;
      }

      // Dismantle everything in the global namespace except for the
      // "errorInfo" and "errorCode" variables. These might be needed
      // later on if errors occur while deleting commands. We are careful
      // to destroy and recreate the "errorInfo" and "errorCode"
      // variables, in case they had any traces on them.
      //
      // Dismantle the namespace here, before we clear the assocData. If any
      // background errors occur here, they will be deleted below.


      // FIXME : check impl of TclTeardownNamespace
      NamespaceCmd.teardownNamespace( globalNs );

      // Delete all variables.

      TclObject errorInfoObj = null, errorCodeObj = null;

      try
      {
        errorInfoObj = getVar( "errorInfo", null, TCL.VarFlag.GLOBAL_ONLY );
      }
      catch ( TclException e )
      {
        // Do nothing when var does not exist.
      }

      if ( errorInfoObj != null )
      {
        errorInfoObj.preserve();
      }

      try
      {
        errorCodeObj = getVar( "errorCode", null, TCL.VarFlag.GLOBAL_ONLY );
      }
      catch ( TclException e )
      {
        // Do nothing when var does not exist.
      }

      if ( errorCodeObj != null )
      {
        errorCodeObj.preserve();
      }

      frame = null;
      varFrame = null;

      try
      {
        if ( errorInfoObj != null )
        {
          setVar( "errorInfo", null, errorInfoObj, TCL.VarFlag.GLOBAL_ONLY );
          errorInfoObj.release();
        }
        if ( errorCodeObj != null )
        {
          setVar( "errorCode", null, errorCodeObj, TCL.VarFlag.GLOBAL_ONLY );
          errorCodeObj.release();
        }
      }
      catch ( TclException e )
      {
        // Ignore it -- same behavior as Tcl 8.0.
      }

      // Tear down the math function table.

      expr = null;

      // Remove all the assoc data tied to this interp and invoke
      // deletion callbacks; note that a callback can create new
      // callbacks, so we iterate.

      // ATK The java code was somethink strong
      if ( assocData != null )
      {
        foreach ( AssocData data in assocData.Values )
        {
          data.disposeAssocData( this );
        }
        assocData.Clear();
      }

      // Close any remaining channels

      for ( IDictionaryEnumerator e = interpChanTable.GetEnumerator(); e.MoveNext(); )
      {
        Object key = e.Key;
        Channel chan = (Channel)e.Value;
        try
        {
          chan.close();
        }
        catch ( IOException ex )
        {
          // Ignore any IO errors
        }
      }

      // Finish deleting the global namespace.

      // FIXME : check impl of Tcl_DeleteNamespace
      NamespaceCmd.deleteNamespace( globalNs );
      globalNs = null;

      // Free up the result *after* deleting variables, since variable
      // deletion could have transferred ownership of the result string
      // to Tcl.

      frame = null;
      varFrame = null;
      resolvers = null;

      resetResult();
    }
    ~Interp()
    {
      dispose();
    }
    protected internal void createCommands()
    {
      Extension.loadOnDemand( this, "after", "tcl.lang.AfterCmd" );
      Extension.loadOnDemand( this, "append", "tcl.lang.AppendCmd" );
      Extension.loadOnDemand( this, "array", "tcl.lang.ArrayCmd" );
      Extension.loadOnDemand( this, "binary", "tcl.lang.BinaryCmd" );
      Extension.loadOnDemand( this, "break", "tcl.lang.BreakCmd" );
      Extension.loadOnDemand( this, "case", "tcl.lang.CaseCmd" );
      Extension.loadOnDemand( this, "catch", "tcl.lang.CatchCmd" );
      Extension.loadOnDemand( this, "cd", "tcl.lang.CdCmd" );
      Extension.loadOnDemand( this, "clock", "tcl.lang.ClockCmd" );
      Extension.loadOnDemand( this, "close", "tcl.lang.CloseCmd" );
      Extension.loadOnDemand( this, "continue", "tcl.lang.ContinueCmd" );
      Extension.loadOnDemand( this, "concat", "tcl.lang.ConcatCmd" );
      Extension.loadOnDemand( this, "encoding", "tcl.lang.EncodingCmd" );
      Extension.loadOnDemand( this, "eof", "tcl.lang.EofCmd" );
      Extension.loadOnDemand( this, "eval", "tcl.lang.EvalCmd" );
      Extension.loadOnDemand( this, "error", "tcl.lang.ErrorCmd" );
      if ( !Util.Mac )
      {
        Extension.loadOnDemand( this, "exec", "tcl.lang.ExecCmd" );
      }
      Extension.loadOnDemand( this, "exit", "tcl.lang.ExitCmd" );
      Extension.loadOnDemand( this, "expr", "tcl.lang.ExprCmd" );
      Extension.loadOnDemand( this, "fblocked", "tcl.lang.FblockedCmd" );
      Extension.loadOnDemand( this, "fconfigure", "tcl.lang.FconfigureCmd" );
      Extension.loadOnDemand( this, "file", "tcl.lang.FileCmd" );
      Extension.loadOnDemand( this, "flush", "tcl.lang.FlushCmd" );
      Extension.loadOnDemand( this, "for", "tcl.lang.ForCmd" );
      Extension.loadOnDemand( this, "foreach", "tcl.lang.ForeachCmd" );
      Extension.loadOnDemand( this, "format", "tcl.lang.FormatCmd" );
      Extension.loadOnDemand( this, "gets", "tcl.lang.GetsCmd" );
      Extension.loadOnDemand( this, "global", "tcl.lang.GlobalCmd" );
      Extension.loadOnDemand( this, "glob", "tcl.lang.GlobCmd" );
      Extension.loadOnDemand( this, "if", "tcl.lang.IfCmd" );
      Extension.loadOnDemand( this, "incr", "tcl.lang.IncrCmd" );
      Extension.loadOnDemand( this, "info", "tcl.lang.InfoCmd" );
      Extension.loadOnDemand( this, "interp", "tcl.lang.InterpCmd" );
      Extension.loadOnDemand( this, "list", "tcl.lang.ListCmd" );
      Extension.loadOnDemand( this, "join", "tcl.lang.JoinCmd" );
      Extension.loadOnDemand( this, "lappend", "tcl.lang.LappendCmd" );
      Extension.loadOnDemand( this, "lindex", "tcl.lang.LindexCmd" );
      Extension.loadOnDemand( this, "linsert", "tcl.lang.LinsertCmd" );
      Extension.loadOnDemand( this, "llength", "tcl.lang.LlengthCmd" );
      Extension.loadOnDemand( this, "lrange", "tcl.lang.LrangeCmd" );
      Extension.loadOnDemand( this, "lreplace", "tcl.lang.LreplaceCmd" );
      Extension.loadOnDemand( this, "lsearch", "tcl.lang.LsearchCmd" );
      Extension.loadOnDemand( this, "lset", "tcl.lang.LsetCmd" );
      Extension.loadOnDemand( this, "lsort", "tcl.lang.LsortCmd" );
      Extension.loadOnDemand( this, "namespace", "tcl.lang.NamespaceCmd" );
      Extension.loadOnDemand( this, "open", "tcl.lang.OpenCmd" );
      Extension.loadOnDemand( this, "package", "tcl.lang.PackageCmd" );
      Extension.loadOnDemand( this, "proc", "tcl.lang.ProcCmd" );
      Extension.loadOnDemand( this, "puts", "tcl.lang.PutsCmd" );
      Extension.loadOnDemand( this, "pwd", "tcl.lang.PwdCmd" );
      Extension.loadOnDemand( this, "read", "tcl.lang.ReadCmd" );
      Extension.loadOnDemand( this, "regsub", "tcl.lang.RegsubCmd" );
      Extension.loadOnDemand( this, "rename", "tcl.lang.RenameCmd" );
      Extension.loadOnDemand( this, "return", "tcl.lang.ReturnCmd" );
      Extension.loadOnDemand( this, "scan", "tcl.lang.ScanCmd" );
      Extension.loadOnDemand( this, "seek", "tcl.lang.SeekCmd" );
      Extension.loadOnDemand( this, "set", "tcl.lang.SetCmd" );
      Extension.loadOnDemand( this, "socket", "tcl.lang.SocketCmd" );
      Extension.loadOnDemand( this, "source", "tcl.lang.SourceCmd" );
      Extension.loadOnDemand( this, "split", "tcl.lang.SplitCmd" );
      Extension.loadOnDemand( this, "string", "tcl.lang.StringCmd" );
      Extension.loadOnDemand( this, "subst", "tcl.lang.SubstCmd" );
      Extension.loadOnDemand( this, "switch", "tcl.lang.SwitchCmd" );
      Extension.loadOnDemand( this, "tell", "tcl.lang.TellCmd" );
      Extension.loadOnDemand( this, "time", "tcl.lang.TimeCmd" );
      Extension.loadOnDemand( this, "trace", "tcl.lang.TraceCmd" );
      Extension.loadOnDemand( this, "unset", "tcl.lang.UnsetCmd" );
      Extension.loadOnDemand( this, "update", "tcl.lang.UpdateCmd" );
      Extension.loadOnDemand( this, "uplevel", "tcl.lang.UplevelCmd" );
      Extension.loadOnDemand( this, "upvar", "tcl.lang.UpvarCmd" );
      Extension.loadOnDemand( this, "variable", "tcl.lang.VariableCmd" );
      Extension.loadOnDemand( this, "vwait", "tcl.lang.VwaitCmd" );
      Extension.loadOnDemand( this, "while", "tcl.lang.WhileCmd" );


      // Add "regexp" and related commands to this interp.
      RegexpCmd.init( this );


      // The Java package is only loaded when the user does a
      // "package require java" in the interp. We need to create a small
      // command that will load when "package require java" is called.

      Extension.loadOnDemand( this, "jaclloadjava", "tcl.lang.JaclLoadJavaCmd" );

      try
      {
        eval( "package ifneeded java 1.3.1 jaclloadjava" );
      }
      catch ( TclException e )
      {
        System.Diagnostics.Debug.WriteLine( getResult().ToString() );
        SupportClass.WriteStackTrace( e, Console.Error );
        throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
      }

    }
    public void setAssocData( string name, AssocData data )
    // Object associated with the name.
    {
      if ( assocData == null )
      {
        assocData = new Hashtable();
      }
      SupportClass.PutElement( assocData, name, data );
    }
    public void deleteAssocData( string name )
    // Name of association.
    {
      if ( assocData == null )
      {
        return;
      }

      SupportClass.HashtableRemove( assocData, name );
    }
    public AssocData getAssocData( string name )
    // Name of association.
    {
      if ( assocData == null )
      {
        return null;
      }
      else
      {
        return (AssocData)assocData[name];
      }
    }

    public void backgroundError()
    {
      BgErrorMgr mgr = (BgErrorMgr)getAssocData( "tclBgError" );
      if ( mgr == null )
      {
        mgr = new BgErrorMgr( this );
        setAssocData( "tclBgError", mgr );
      }
      mgr.addBgError();
    }

    /*-----------------------------------------------------------------
    *
    *                        VARIABLES
    *
    *-----------------------------------------------------------------
    */
    public TclObject setVar( TclObject nameObj, TclObject value, TCL.VarFlag flags )
    {
      return Var.setVar( this, nameObj, value, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public TclObject setVar( string name, TclObject value, TCL.VarFlag flags )
    {
      return Var.setVar( this, name, value, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public TclObject setVar( string name1, string name2, TclObject value, TCL.VarFlag flags )
    {
      return Var.setVar( this, name1, name2, value, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public void setVar( string name, string strValue, TCL.VarFlag flags )
    {
      Var.setVar( this, name, TclString.newInstance( strValue ), ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public void setVar( string name1, string name2, string strValue, TCL.VarFlag flags )
    {
      Var.setVar( this, name1, name2, TclString.newInstance( strValue ), ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public TclObject getVar( TclObject nameObj, TCL.VarFlag flags )
    {
      return Var.getVar( this, nameObj, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public TclObject getVar( string name, TCL.VarFlag flags )
    {
      return Var.getVar( this, name, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public TclObject getVar( string name1, string name2, TCL.VarFlag flags )
    {
      return Var.getVar( this, name1, name2, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public void unsetVar( TclObject nameObj, TCL.VarFlag flags )
    {
      Var.unsetVar( this, nameObj, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public void unsetVar( string name, TCL.VarFlag flags )
    {
      Var.unsetVar( this, name, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public void unsetVar( string name1, string name2, TCL.VarFlag flags )
    {
      Var.unsetVar( this, name1, name2, ( flags | TCL.VarFlag.LEAVE_ERR_MSG ) );
    }
    public void traceVar( TclObject nameObj, VarTrace trace, TCL.VarFlag flags )
    {
      Var.traceVar( this, nameObj, flags, trace );
    }
    public void traceVar( string name, VarTrace trace, TCL.VarFlag flags )
    {
      Var.traceVar( this, name, flags, trace );
    }
    public void traceVar( string part1, string part2, VarTrace trace, TCL.VarFlag flags )
    {
      Var.traceVar( this, part1, part2, flags, trace );
    }
    public void untraceVar( TclObject nameObj, VarTrace trace, TCL.VarFlag flags )
    // OR-ed collection of bits describing current
    // trace, including any of TCL.VarFlag.TRACE_READS,
    // TCL.VarFlag.TRACE_WRITES, TCL.VarFlag.TRACE_UNSETS,
    // TCL.VarFlag.GLOBAL_ONLY and TCL.VarFlag.NAMESPACE_ONLY.
    {
      Var.untraceVar( this, nameObj, flags, trace );
    }
    public void untraceVar( string name, VarTrace trace, TCL.VarFlag flags )
    // OR-ed collection of bits describing current
    // trace, including any of TCL.VarFlag.TRACE_READS,
    // TCL.VarFlag.TRACE_WRITES, TCL.VarFlag.TRACE_UNSETS,
    // TCL.VarFlag.GLOBAL_ONLY and TCL.VarFlag.NAMESPACE_ONLY.
    {
      Var.untraceVar( this, name, flags, trace );
    }
    public void untraceVar( string part1, string part2, VarTrace trace, TCL.VarFlag flags )
    // OR-ed collection of bits describing current
    // trace, including any of TCL.VarFlag.TRACE_READS,
    // TCL.VarFlag.TRACE_WRITES, TCL.VarFlag.TRACE_UNSETS,
    // TCL.VarFlag.GLOBAL_ONLY and TCL.VarFlag.NAMESPACE_ONLY.
    {
      Var.untraceVar( this, part1, part2, flags, trace );
    }
    public void createCommand( string cmdName, Command cmdImpl )
    // Command object to associate with
    // cmdName.
    {
      ImportRef oldRef = null;
      NamespaceCmd.Namespace ns;
      WrappedCommand cmd, refCmd;
      string tail;
      ImportedCmdData data;

      if ( deleted )
      {
        // The interpreter is being deleted.  Don't create any new
        // commands; it's not safe to muck with the interpreter anymore.

        return;
      }

      // Determine where the command should reside. If its name contains 
      // namespace qualifiers, we put it in the specified namespace; 
      // otherwise, we always put it in the global namespace.

      if ( cmdName.IndexOf( "::" ) != -1 )
      {
        // Java does not support passing an address so we pass
        // an array of size 1 and then assign arr[0] to the value
        NamespaceCmd.Namespace[] nsArr = new NamespaceCmd.Namespace[1];
        NamespaceCmd.Namespace[] dummyArr = new NamespaceCmd.Namespace[1];
        string[] tailArr = new string[1];

        NamespaceCmd.getNamespaceForQualName( this, cmdName, null, TCL.VarFlag.CREATE_NS_IF_UNKNOWN, nsArr, dummyArr, dummyArr, tailArr );

        ns = nsArr[0];
        tail = tailArr[0];

        if ( ( ns == null ) || ( (System.Object)tail == null ) )
        {
          return;
        }
      }
      else
      {
        ns = globalNs;
        tail = cmdName;
      }

      cmd = (WrappedCommand)ns.cmdTable[tail];
      if ( cmd != null )
      {
        // Command already exists. Delete the old one.
        // Be careful to preserve any existing import links so we can
        // restore them down below.  That way, you can redefine a
        // command and its import status will remain intact.

        oldRef = cmd.importRef;
        cmd.importRef = null;

        deleteCommandFromToken( cmd );

        // FIXME : create a test case for this condition!

        cmd = (WrappedCommand)ns.cmdTable[tail];
        if ( cmd != null )
        {
          // If the deletion callback recreated the command, just throw
          // away the new command (if we try to delete it again, we
          // could get stuck in an infinite loop).

          SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
        }
      }

      cmd = new WrappedCommand();
      ns.cmdTable.Add( tail, cmd );
      cmd.table = ns.cmdTable;
      cmd.hashKey = tail;
      cmd.ns = ns;
      cmd.cmd = cmdImpl;
      cmd.deleted = false;
      // FIXME : import feature not implemented
      //cmd.importRef = null;

      // Plug in any existing import references found above.  Be sure
      // to update all of these references to point to the new command.

      if ( oldRef != null )
      {
        cmd.importRef = oldRef;
        while ( oldRef != null )
        {
          refCmd = oldRef.importedCmd;
          data = (ImportedCmdData)refCmd.cmd;
          data.realCmd = cmd;
          oldRef = oldRef.next;
        }
      }

      // There are no shadowed commands in Jacl because they are only
      // used in the 8.0 compiler

      return;
    }
    /*
     *----------------------------------------------------------------------
     *
     * Tcl_CreateObjCommand --
     *
     *  Define a new object-based command in a command table.
     *
     * Results:
     *  The return value is a token for the command, which can
     *  be used in future calls to Tcl_GetCommandName.
     *
     * Side effects:
     *  If no command named "cmdName" already exists for interp, one is
     *  created. Otherwise, if a command does exist, then if the
     *  object-based Tcl_ObjCmdProc is TclInvokeStringCommand, we assume
     *  Tcl_CreateCommand was called previously for the same command and
     *  just set its Tcl_ObjCmdProc to the argument "proc"; otherwise, we
     *  delete the old command.
     *
     *  In the future, during bytecode evaluation when "cmdName" is seen as
     *  the name of a command by Tcl_EvalObj or Tcl_Eval, the object-based
     *  Tcl_ObjCmdProc proc will be called. When the command is deleted from
     *  the table, deleteProc will be called. See the manual entry for
     *  details on the calling sequence.
     *
     *----------------------------------------------------------------------
     */

    public delegate int dxObjCmdProc( object clientData, Interp interp, int argc, TclObject[] argv );
    public delegate void dxCmdDeleteProc( ref object clientData );

    public void createObjCommand( string cmdName, dxObjCmdProc proc, object clientData, dxCmdDeleteProc deleteProc )
    // Command object to associate with cmdName.
    {
      ImportRef oldRef = null;
      NamespaceCmd.Namespace ns;
      WrappedCommand cmd, refCmd;
      string tail;
      ImportedCmdData data;
      int _new;

      if ( deleted )
      {
        // The interpreter is being deleted.  Don't create any new
        // commands; it's not safe to muck with the interpreter anymore.

        return;
      }

      // Determine where the command should reside. If its name contains 
      // namespace qualifiers, we put it in the specified namespace; 
      // otherwise, we always put it in the global namespace.

      if ( cmdName.IndexOf( "::" ) != -1 )
      {
        // Java does not support passing an address so we pass
        // an array of size 1 and then assign arr[0] to the value
        NamespaceCmd.Namespace[] nsArr = new NamespaceCmd.Namespace[1];
        NamespaceCmd.Namespace[] dummyArr = new NamespaceCmd.Namespace[1];
        string[] tailArr = new string[1];

        NamespaceCmd.getNamespaceForQualName( this, cmdName, null, TCL.VarFlag.CREATE_NS_IF_UNKNOWN, nsArr, dummyArr, dummyArr, tailArr );

        ns = nsArr[0];
        tail = tailArr[0];

        if ( ( ns == null ) || ( (System.Object)tail == null ) )
        {
          return;
        }
      }
      else
      {
        ns = globalNs;
        tail = cmdName;
      }

      cmd = (WrappedCommand)ns.cmdTable[tail];
      if ( cmd != null )
      {
        /*
 * Command already exists. If its object-based Tcl_ObjCmdProc is
 * TclInvokeStringCommand, we just set its Tcl_ObjCmdProc to the
 * argument "proc". Otherwise, we delete the old command. 
 */
        if ( cmd.objProc != null && cmd.objProc.GetType().Name == "TclInvokeStringCommand" )
        {
          cmd.objProc = proc;
          cmd.objClientData = clientData;
          cmd.deleteProc = deleteProc;
          cmd.deleteData = clientData;
          return;
        }
        /*
         * Otherwise, we delete the old command.  Be careful to preserve
         * any existing import links so we can restore them down below.
         * That way, you can redefine a command and its import status
         * will remain intact.
         */
        oldRef = cmd.importRef;
        cmd.importRef = null;

        deleteCommandFromToken( cmd );

        // FIXME : create a test case for this condition!

        cmd = (WrappedCommand)ns.cmdTable[tail];
        if ( cmd != null )
        {
          // If the deletion callback recreated the command, just throw
          // away the new command (if we try to delete it again, we
          // could get stuck in an infinite loop).

          SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
        }
      }

      cmd = new WrappedCommand();
      ns.cmdTable.Add( tail, cmd );
      cmd.table = ns.cmdTable;
      cmd.hashKey = tail;
      cmd.ns = ns;
      cmd.cmd = null;
      cmd.deleted = false;
      // FIXME : import feature not implemented
      //cmd.importRef = null;

      // TODO -- Determine if this is all correct
      cmd.objProc = proc;
      cmd.objClientData = clientData;
      //cmd.proc = TclInvokeObjectCommand;
      cmd.clientData = (object)cmd;
      cmd.deleteProc = deleteProc;
      cmd.deleteData = clientData;
      cmd.flags = 0;


      // Plug in any existing import references found above.  Be sure
      // to update all of these references to point to the new command.

      if ( oldRef != null )
      {
        cmd.importRef = oldRef;
        while ( oldRef != null )
        {
          refCmd = oldRef.importedCmd;
          data = (ImportedCmdData)refCmd.cmd;
          data.realCmd = cmd;
          oldRef = oldRef.next;
        }
      }

      // There are no shadowed commands in Jacl because they are only
      // used in the 8.0 compiler

      return;
    }
    internal string getCommandFullName( WrappedCommand cmd )
    // Token for the command.
    {
      Interp interp = this;
      StringBuilder name = new StringBuilder();

      // Add the full name of the containing namespace, followed by the "::"
      // separator, and the command name.

      if ( cmd != null )
      {
        if ( cmd.ns != null )
        {
          name.Append( cmd.ns.fullName );
          if ( cmd.ns != interp.globalNs )
          {
            name.Append( "::" );
          }
        }
        if ( cmd.table != null )
        {
          name.Append( cmd.hashKey );
        }
      }

      return name.ToString();
    }
    public int deleteCommand( string cmdName )
    // Name of command to remove.
    {
      WrappedCommand cmd;

      //  Find the desired command and delete it.

      try
      {
        cmd = NamespaceCmd.findCommand( this, cmdName, null, 0 );
      }
      catch ( TclException e )
      {
        throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
      }
      if ( cmd == null )
      {
        return -1;
      }
      if ( cmd.deleteProc != null )
        cmd.deleteProc( ref cmd.deleteData );
      return deleteCommandFromToken( cmd );
    }
    protected internal int deleteCommandFromToken( WrappedCommand cmd )
    // Wrapper Token for command to delete.
    {
      if ( cmd == null )
      {
        return -1;
      }

      ImportRef ref_Renamed, nextRef;
      WrappedCommand importCmd;

      // The code here is tricky.  We can't delete the hash table entry
      // before invoking the deletion callback because there are cases
      // where the deletion callback needs to invoke the command (e.g.
      // object systems such as OTcl). However, this means that the
      // callback could try to delete or rename the command. The deleted
      // flag allows us to detect these cases and skip nested deletes.

      if ( cmd.deleted )
      {
        // Another deletion is already in progress.  Remove the hash
        // table entry now, but don't invoke a callback or free the
        // command structure.

        if ( (System.Object)cmd.hashKey != null && cmd.table != null )
        {
          SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
          cmd.table = null;
          cmd.hashKey = null;
        }
        return 0;
      }

      cmd.deleted = true;
      if ( cmd.cmd is CommandWithDispose )
      {
        ( (CommandWithDispose)cmd.cmd ).disposeCmd();
      }
      if ( cmd.deleteProc != null )
      {
        cmd.deleteProc( ref cmd.objClientData );
      }
      // If this command was imported into other namespaces, then imported
      // commands were created that refer back to this command. Delete these
      // imported commands now.

      for ( ref_Renamed = cmd.importRef; ref_Renamed != null; ref_Renamed = nextRef )
      {
        nextRef = ref_Renamed.next;
        importCmd = ref_Renamed.importedCmd;
        deleteCommandFromToken( importCmd );
      }

      // FIXME : what does this mean? Is this a mistake in the C comment?

      // Don't use hPtr to delete the hash entry here, because it's
      // possible that the deletion callback renamed the command.
      // Instead, use cmdPtr->hptr, and make sure that no-one else
      // has already deleted the hash entry.

      if ( cmd.table != null )
      {
        SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
        cmd.table = null;
        cmd.hashKey = null;
      }

      // Drop the reference to the Command instance inside the WrappedCommand

      cmd.cmd = null;

      // We do not need to cleanup the WrappedCommand because GC will get it.

      return 0;
    }
    protected internal void renameCommand( string oldName, string newName )
    {
      Interp interp = this;
      string newTail;
      NamespaceCmd.Namespace cmdNs, newNs;
      WrappedCommand cmd;
      Hashtable table, oldTable;
      string hashKey, oldHashKey;

      // Find the existing command. An error is returned if cmdName can't
      // be found.

      cmd = NamespaceCmd.findCommand( interp, oldName, null, 0 );
      if ( cmd == null )
      {
        throw new TclException( interp, "can't " + ( ( ( (System.Object)newName == null ) || ( newName.Length == 0 ) ) ? "delete" : "rename" ) + " \"" + oldName + "\": command doesn't exist" );
      }
      cmdNs = cmd.ns;

      // If the new command name is NULL or empty, delete the command. Do this
      // with Tcl_DeleteCommandFromToken, since we already have the command.

      if ( ( (System.Object)newName == null ) || ( newName.Length == 0 ) )
      {
        deleteCommandFromToken( cmd );
        return;
      }

      // Make sure that the destination command does not already exist.
      // The rename operation is like creating a command, so we should
      // automatically create the containing namespaces just like
      // Tcl_CreateCommand would.

      NamespaceCmd.Namespace[] newNsArr = new NamespaceCmd.Namespace[1];
      NamespaceCmd.Namespace[] dummyArr = new NamespaceCmd.Namespace[1];
      string[] newTailArr = new string[1];

      NamespaceCmd.getNamespaceForQualName( interp, newName, null, TCL.VarFlag.CREATE_NS_IF_UNKNOWN, newNsArr, dummyArr, dummyArr, newTailArr );

      newNs = newNsArr[0];
      newTail = newTailArr[0];

      if ( ( newNs == null ) || ( (System.Object)newTail == null ) )
      {
        throw new TclException( interp, "can't rename to \"" + newName + "\": bad command name" );
      }
      if ( newNs.cmdTable[newTail] != null )
      {
        throw new TclException( interp, "can't rename to \"" + newName + "\": command already exists" );
      }

      // Warning: any changes done in the code here are likely
      // to be needed in Tcl_HideCommand() code too.
      // (until the common parts are extracted out)     --dl

      // Put the command in the new namespace so we can check for an alias
      // loop. Since we are adding a new command to a namespace, we must
      // handle any shadowing of the global commands that this might create.

      oldTable = cmd.table;
      oldHashKey = cmd.hashKey;
      newNs.cmdTable.Add( newTail, cmd );
      cmd.table = newNs.cmdTable;
      cmd.hashKey = newTail;
      cmd.ns = newNs;

      // FIXME : this is a nasty hack that fixes renaming for Procedures
      // that move from one namespace to another, but the real problem
      // is that a rename does not work for Command instances in general

      if ( cmd.cmd is Procedure )
      {
        Procedure p = (Procedure)cmd.cmd;
        p.ns = cmd.ns;
      }

      // Now check for an alias loop. If we detect one, put everything back
      // the way it was and report the error.

      try
      {
        interp.preventAliasLoop( interp, cmd );
      }
      catch ( TclException e )
      {
        newNs.cmdTable.Remove( newTail );
        cmd.table = oldTable;
        cmd.hashKey = oldHashKey;
        cmd.ns = cmdNs;
        throw;
      }

      // The new command name is okay, so remove the command from its
      // current namespace. This is like deleting the command, so bump
      // the cmdEpoch to invalidate any cached references to the command.

      SupportClass.HashtableRemove( oldTable, oldHashKey );

      return;
    }
    internal void preventAliasLoop( Interp cmdInterp, WrappedCommand cmd )
    {
      // If we are not creating or renaming an alias, then it is
      // always OK to create or rename the command.

      if ( !( cmd.cmd is InterpAliasCmd ) )
      {
        return;
      }

      // OK, we are dealing with an alias, so traverse the chain of aliases.
      // If we encounter the alias we are defining (or renaming to) any in
      // the chain then we have a loop.

      InterpAliasCmd alias = (InterpAliasCmd)cmd.cmd;
      InterpAliasCmd nextAlias = alias;
      while ( true )
      {

        // If the target of the next alias in the chain is the same as
        // the source alias, we have a loop.

        WrappedCommand aliasCmd = nextAlias.getTargetCmd( this );
        if ( aliasCmd == null )
        {
          return;
        }
        if ( aliasCmd.cmd == cmd.cmd )
        {

          throw new TclException( this, "cannot define or rename alias \"" + alias.name + "\": would create a loop" );
        }

        // Otherwise, follow the chain one step further. See if the target
        // command is an alias - if so, follow the loop to its target
        // command. Otherwise we do not have a loop.

        if ( !( aliasCmd.cmd is InterpAliasCmd ) )
        {
          return;
        }
        nextAlias = (InterpAliasCmd)aliasCmd.cmd;
      }
    }
    public Command getCommand( string cmdName )
    // String name of the command.
    {
      //  Find the desired command and return it.

      WrappedCommand cmd;

      try
      {
        cmd = NamespaceCmd.findCommand( this, cmdName, null, 0 );
      }
      catch ( TclException e )
      {
        throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
      }

      return ( ( cmd == null ) ? null : cmd.cmd );
    }
    public WrappedCommand getObjCommand( string cmdName )
    // String name of the command.
    {
      //  Find the desired command and return it.

      WrappedCommand cmd;

      try
      {
        cmd = NamespaceCmd.findCommand( this, cmdName, null, 0 );
      }
      catch ( TclException e )
      {
        throw new TclRuntimeError( "unexpected TclException: " + e.Message, e );
      }

      return ( ( cmd == null ) ? null : cmd );
    }
    public static bool commandComplete( string inString )
    // The string to check.
    {
      return Parser.commandComplete( inString, inString.Length );
    }


    /*-----------------------------------------------------------------
    *
    *                        EVAL
    *
    *-----------------------------------------------------------------
    */

    public TclObject getResult()
    {
      return m_result;
    }
    public void setResult( TclObject r )
    // A Tcl Object to be set as the result.
    {
      if ( r == null )
      {
        throw new System.NullReferenceException( "Interp.setResult() called with null TclObject argument." );
      }

      if ( r == m_result )
      {
        // Setting to current value (including m_nullResult) is a no-op.
        return;
      }

      if ( m_result != m_nullResult )
      {
        m_result.release();
      }

      m_result = r;

      if ( m_result != m_nullResult )
      {
        m_result.preserve();
      }
    }
    public void setResult( string r )
    // A string result.
    {
      if ( (System.Object)r == null )
      {
        resetResult();
      }
      else
      {
        setResult( TclString.newInstance( r ) );
      }
    }
    public void setResult( int r )
    // An int result.
    {
      setResult( TclInteger.newInstance( r ) );
    }
    public void setResult( double r )
    // A double result.
    {
      setResult( TclDouble.newInstance( r ) );
    }
    public void setResult( bool r )
    // A boolean result.
    {
      setResult( TclBoolean.newInstance( r ) );
    }
    public void resetResult()
    {
      if ( m_result != m_nullResult )
      {
        m_result.release();
        m_result = TclString.newInstance( "" ); //m_nullResult;
        m_result.preserve();
        if ( !m_nullResult.Shared )
        {
          throw new TclRuntimeError( "m_nullResult is not shared" );
        }
      }
      errAlreadyLogged = false;
      errInProgress = false;
      errCodeSet = false;
      returnCode = TCL.CompletionCode.OK;
    }
    public void appendElement( object Element )
    {
      TclObject result;

      result = getResult();
      if ( result.Shared )
      {
        result = result.duplicate();
      }
      TclList.append( this, result, TclObj.newInstance( Element ) );
      setResult( result );
    }

    public void appendElement(
      string Element )
    {
      TclObject result;

      result = getResult();
      if ( result.Shared )
      {
        result = result.duplicate();
      }
      TclList.append( this, result, TclString.newInstance( Element ) );
      setResult( result );
    }
    public void eval( string inString, int flags )
    {
      int evalFlags = this.evalFlags;
      this.evalFlags &= ~Parser.TCL_ALLOW_EXCEPTIONS;

      CharPointer script = new CharPointer( inString );
      try
      {
        Parser.eval2( this, script.array, script.index, script.length(), flags );
      }
      catch ( TclException e )
      {

        if ( nestLevel != 0 )
        {
          throw;
        }

        // Update the interpreter's evaluation level count. If we are again at
        // the top level, process any unusual return code returned by the
        // evaluated code. Note that we don't propagate an exception that
        // has a TCL.CompletionCode.RETURN error code when updateReturnInfo() returns TCL.CompletionCode.OK.

        TCL.CompletionCode result = e.getCompletionCode();

        if ( result == TCL.CompletionCode.RETURN )
        {
          result = updateReturnInfo();
        }
        if ( result != TCL.CompletionCode.EXIT && result != TCL.CompletionCode.OK && result != TCL.CompletionCode.ERROR && ( evalFlags & Parser.TCL_ALLOW_EXCEPTIONS ) == 0 )
        {
          processUnexpectedResult( result );
        }
        if ( result != TCL.CompletionCode.OK )
        {
          e.setCompletionCode( result );
          throw;
        }
      }
    }
    public void eval( string script )
    {
      eval( script, 0 );
    }
    public void eval( TclObject tobj, int flags )
    {

      eval( tobj.ToString(), flags );
    }
    public void recordAndEval( TclObject script, int flags )
    {
      // Append the script to the event list by calling "history add <script>".
      // We call the eval method with the command of type TclObject, so that
      // we don't have to deal with funny chars ("{}[]$\) in the script.

      TclObject cmd = null;
      try
      {
        cmd = TclList.newInstance();
        TclList.append( this, cmd, TclString.newInstance( "history" ) );
        TclList.append( this, cmd, TclString.newInstance( "add" ) );
        TclList.append( this, cmd, script );
        cmd.preserve();
        eval( cmd, TCL.EVAL_GLOBAL );
      }
      catch ( System.Exception e )
      {
      }
      finally
      {
        cmd.release();
      }

      // Execute the command.

      if ( ( flags & TCL.NO_EVAL ) == 0 )
      {
        eval( script, flags & TCL.EVAL_GLOBAL );
      }
    }
    public void evalFile( string sFilename )
    {
      string fileContent; // Contains the content of the file.

      fileContent = readScriptFromFile( sFilename );

      if ( (System.Object)fileContent == null )
      {
        throw new TclException( this, "couldn't read file \"" + sFilename + "\"" );
      }

      string oldScript = scriptFile;
      scriptFile = sFilename;

      try
      {
        pushDebugStack( sFilename, 1 );
        eval( fileContent, 0 );
      }
      catch ( TclException e )
      {
        if ( e.getCompletionCode() == TCL.CompletionCode.ERROR )
        {
          addErrorInfo( "\n    (file \"" + sFilename + "\" line " + errorLine + ")" );
        }
        throw;
      }
      finally
      {
        scriptFile = oldScript;
        popDebugStack();
      }
    }
    internal void evalURL( System.Uri context, string s )
    {
      string fileContent; // Contains the content of the file.

      fileContent = readScriptFromURL( context, s );
      if ( (System.Object)fileContent == null )
      {
        throw new TclException( this, "cannot read URL \"" + s + "\"" );
      }

      string oldScript = scriptFile;
      scriptFile = s;

      try
      {
        eval( fileContent, 0 );
      }
      finally
      {
        scriptFile = oldScript;
      }
    }
    private string readScriptFromFile( string sFilename )
    // The name of the file.
    {
      FileInfo sourceFile;
      StreamReader fs;
      try
      {
        sourceFile = FileUtil.getNewFileObj( this, sFilename );
      }
      catch ( TclException e )
      {
        resetResult();
        return null;
      }
      catch ( FileNotFoundException e )
      {
        return null;
      }
      catch ( System.Security.SecurityException sec_e )
      {
        return null;
      }
      try
      {
        // HACK only UTF8 will be read
        using ( fs = new StreamReader( sourceFile.FullName, System.Text.Encoding.UTF8 ) )
        {
          // read all an do the new line conversations
          return fs.ReadToEnd().Replace( "\r\n", "\n" );
        }
      }
      catch ( IOException )
      {
        return null;
      }
    }
    private string readScriptFromURL( System.Uri context, string s )
    {
      Object content = null;
      System.Uri url;

      try
      {
        url = new System.Uri( context, s );
      }
      catch ( System.UriFormatException e )
      {
        return null;
      }

      try
      {

        // ATK content = url.getContent();
        content = url.ToString();
      }
      catch ( System.Exception e )
      {
        Type jar_class;

        try
        {
          jar_class = System.Type.GetType( "java.net.JarURLConnection" );
        }
        catch ( System.Exception e2 )
        {
          return null;
        }

        Object jar;
        try
        {
          jar = (System.Net.HttpWebRequest)System.Net.WebRequest.Create( url );
        }
        catch ( IOException e2 )
        {
          return null;
        }

        if ( jar == null )
        {
          return null;
        }

        // We must call JarURLConnection.getInputStream() dynamically
        // Because the class JarURLConnection does not exist in JDK1.1

        try
        {
          System.Reflection.MethodInfo m = jar_class.GetMethod( "openConnection", (System.Type[])null );
          content = m.Invoke( jar, (System.Object[])null );
        }
        catch ( System.Exception e2 )
        {
          return null;
        }
      }
      // HACK
      //                        catch (IOException e)
      //                        {
      //                                return null;
      //                        }
      //                        catch (System.Security.SecurityException e)
      //                        {
      //                                return null;
      //                        }

      if ( content is string )
      {
        return (string)content;
      }
      else if ( content is Stream )
      {
        // FIXME : use custom stream handler
        Stream fs = (Stream)content;

        try
        {
          // FIXME : read does not check return values
          long available;
          available = fs.Length - fs.Position;
          byte[] charArray = new byte[(int)available];
          SupportClass.ReadInput( fs, ref charArray, 0, charArray.Length );
          return new string( SupportClass.ToCharArray( charArray ) );
        }
        catch ( IOException e2 )
        {
          return null;
        }
        finally
        {
          closeInputStream( fs );
        }
      }
      else
      {
        return null;
      }
    }
    private void closeInputStream( Stream fs )
    {
      try
      {
        fs.Close();
      }
      catch ( IOException e )
      {
        ;
      }
    }
    internal void evalResource( string resName )
    {
      //                        Stream stream = null;
      //                        
      //                        try
      //                        {
      //                                
      //                                stream = typeof(Interp).getResourceAsStream(resName);
      //                        }
      //                        catch (System.Security.SecurityException e2)
      //                        {
      //                                // This catch is necessary if Jacl is to work in an applet
      //                                // at all. Note that java::new will not work from within Jacl
      //                                // in an applet.
      //                                
      //                                System.Console.Error.WriteLine("evalResource: Ignoring SecurityException, " + "it is likely we are running in an applet: " + "cannot read resource \"" + resName + "\"" + e2);
      //                                
      //                                return ;
      //                        }
      //                        
      //                        if (stream == null)
      //                        {
      //                                throw new TclException(this, "cannot read resource \"" + resName + "\"");
      //                        }
      //                        
      //                        try
      //                        {
      //                                // FIXME : ugly JDK 1.2 only hack
      //                                // Ugly workaround for compressed files BUG in JDK1.2
      //                                // this bug first showed up in  JDK1.2beta4. I have sent
      //                                // a number of emails to Sun but they have deemed this a "feature"
      //                                // of 1.2. This is flat out wrong but I do not seem to change thier
      //                                // minds. Because of this, there is no way to do non blocking IO
      //                                // on a compressed Stream in Java. (mo)
      //                                
      //                                
      //                                if (System_Renamed.getProperty("java.version").StartsWith("1.2") && stream.GetType().FullName.Equals("java.util.zip.ZipFile$1"))
      //                                {
      //                                        
      MemoryStream baos = new MemoryStream( 1024 );
      byte[] buffer = new byte[1024];
      //                                        int numRead;
      //                                        
      //                                        // Read all data from the stream into a resizable buffer
      //                                        while ((numRead = SupportClass.ReadInput(stream, ref buffer, 0, buffer.Length)) != - 1)
      //                                        {
      //                                                baos.Write(SupportClass.ToByteArray(buffer), 0, numRead);
      //                                        }
      //                                        
      //                                        // Convert bytes into a String and eval them
      //                                        eval(new string(SupportClass.ToCharArray(SupportClass.ToByteArray(SupportClass.ToSByteArray(baos.ToArray())))), 0);
      //                                }
      //                                else
      //                                {
      //                                        // Other systems do not need the compressed jar hack
      //                                        
      //                                        long available;
      //                                        available = stream.Length - stream.Position;
      //                                        int num = (int) available;
      //          byte[] byteArray = new byte[num];
      //                                        int offset = 0;
      //                                        while (num > 0)
      //                                        {
      //                                                int readLen = SupportClass.ReadInput(stream, ref byteArray, offset, num);
      //                                                offset += readLen;
      //                                                num -= readLen;
      //                                        }
      //                                        
      //                                        eval(new string(SupportClass.ToCharArray(SupportClass.ToByteArray(byteArray))), 0);
      //                                }
      //                        }
      //                        catch (IOException e)
      //                        {
      //                                return ;
      //                        }
      //                        finally
      //                        {
      //                                closeInputStream(stream);
      //                        }
    }
    internal static BackSlashResult backslash( string s, int i, int len )
    {
      CharPointer script = new CharPointer( s.Substring( 0, ( len ) - ( 0 ) ) );
      script.index = i;
      return Parser.backslash( script.array, script.index );
    }


    public void setErrorCode( TclObject code )
    // The errorCode object.
    {
      try
      {
        setVar( "errorCode", null, code, TCL.VarFlag.GLOBAL_ONLY );
        errCodeSet = true;
      }
      catch ( TclException excp )
      {
        // Ignore any TclException's, possibly caused by variable traces on
        // the errorCode variable. This is compatible with the behavior of
        // the Tcl C API.
      }
    }


    public void addErrorInfo( string message )
    // The message to record.
    {
      if ( !errInProgress )
      {
        errInProgress = true;

        try
        {

          setVar( "errorInfo", null, getResult().ToString(), TCL.VarFlag.GLOBAL_ONLY );
        }
        catch ( TclException e1 )
        {
          // Ignore (see try-block above).
        }

        // If the errorCode variable wasn't set by the code
        // that generated the error, set it to "NONE".

        if ( !errCodeSet )
        {
          try
          {
            setVar( "errorCode", null, "NONE", TCL.VarFlag.GLOBAL_ONLY );
          }
          catch ( TclException e1 )
          {
            // Ignore (see try-block above).
          }
        }
      }

      try
      {
        setVar( "errorInfo", null, message, TCL.VarFlag.APPEND_VALUE | TCL.VarFlag.GLOBAL_ONLY );
      }
      catch ( TclException e1 )
      {
        // Ignore (see try-block above).
      }
    }
    internal void processUnexpectedResult( TCL.CompletionCode returnCode )
    {
      resetResult();
      if ( returnCode == TCL.CompletionCode.BREAK )
      {
        throw new TclException( this, "invoked \"break\" outside of a loop" );
      }
      else if ( returnCode == TCL.CompletionCode.CONTINUE )
      {
        throw new TclException( this, "invoked \"continue\" outside of a loop" );
      }
      else
      {
        throw new TclException( this, "command returned bad code: " + returnCode );
      }
    }
    public TCL.CompletionCode updateReturnInfo()
    {
      TCL.CompletionCode code;

      code = returnCode;
      returnCode = TCL.CompletionCode.OK;

      if ( code == TCL.CompletionCode.ERROR )
      {
        try
        {
          setVar( "errorCode", null, ( (System.Object)errorCode != null ) ? errorCode : "NONE", TCL.VarFlag.GLOBAL_ONLY );
        }
        catch ( TclException e )
        {
          // An error may happen during a trace to errorCode. We ignore it.
          // This may leave error messages inside Interp.result (which
          // is compatible with Tcl 8.0 behavior.
        }
        errCodeSet = true;

        if ( (System.Object)errorInfo != null )
        {
          try
          {
            setVar( "errorInfo", null, errorInfo, TCL.VarFlag.GLOBAL_ONLY );
          }
          catch ( TclException e )
          {
            // An error may happen during a trace to errorInfo. We
            // ignore it.  This may leave error messages inside
            // Interp.result (which is compatible with Tcl 8.0
            // behavior.
          }
          errInProgress = true;
        }
      }

      return code;
    }
    protected internal CallFrame newCallFrame( Procedure proc, TclObject[] objv )
    {
      return new CallFrame( this, proc, objv );
    }
    protected internal CallFrame newCallFrame()
    {
      return new CallFrame( this );
    }
    internal FileInfo getWorkingDir()
    {
      if ( workingDir == null )
      {
        try
        {

          string dirName = getVar( "env", "HOME", 0 ).ToString();
          workingDir = FileUtil.getNewFileObj( this, dirName );
        }
        catch ( TclException e )
        {
          resetResult();
        }
        workingDir = new FileInfo( Util.tryGetSystemProperty( "user.home", "." ) );
      }
      return workingDir;
    }
    internal void setWorkingDir( string dirName )
    {
      FileInfo dirObj = FileUtil.getNewFileObj( this, dirName );

      //  Use the canonical name of the path, if possible.

      try
      {
        dirObj = new FileInfo( dirObj.FullName );
      }
      catch ( IOException e )
      {
      }


      if ( Directory.Exists( dirObj.FullName ) )
      {
        workingDir = dirObj;
      }
      else
      {
        throw new TclException( this, "couldn't change working directory to \"" + dirObj.Name + "\": no such file or directory" );
      }
    }

    public Notifier getNotifier()
    {
      return notifier;
    }
    public void pkgProvide( string name, string version )
    {
      PackageCmd.pkgProvide( this, name, version );
    }
    public string pkgRequire( string pkgname, string version, bool exact )
    {
      return PackageCmd.pkgRequire( this, pkgname, version, exact );
    }

    /*
    * Debugging API.
    *
    * The following section defines two debugging API functions for
    * logging information about the point of execution of Tcl scripts:
    *
    * - pushDebugStack() is called when a procedure body is
    *       executed, or when a file is source'd.
    *      - popDebugStack() is called when the flow of control is about
    *       to return from a procedure body, or from a source'd file.
    *
    * Two other API functions are used to determine the current point of
    * execution:
    *
    *      - getScriptFile() returns the script file current being executed.
    *      - getArgLineNumber(i) returns the line number of the i-th argument
    *        of the current command.
    *
    * Note: The point of execution is automatically maintained for
    *       control structures such as while, if, for and foreach,
    *        as long as they use Interp.eval(argv[?]) to evaluate control
    *        blocks.
    *       
    *        The case and switch commands need to set dbg.cmdLine explicitly
    *        because they may evaluate control blocks that are not elements
    *        inside the argv[] array. ** This feature not yet implemented. **
    *
    *        The proc command needs to call getScriptFile() and
    *       getArgLineNumber(3) to find out the location of the proc
    *       body.
    *
    * The debugging API functions in the Interp class are just dummy stub
    * functions. These functions are usually implemented in a subclass of
    * Interp (e.g. DbgInterp) that has real debugging support.
    *
    */

    protected internal DebugInfo dbg;

    /// <summary> Initialize the debugging information.</summary>
    /// <returns> a DebugInfo object used by Interp in non-debugging mode.
    /// </returns>
    protected internal DebugInfo initDebugInfo()
    {
      return new DebugInfo( null, 1 );
    }

    /// <summary> Add more more level at the top of the debug stack.
    /// 
    /// </summary>
    /// <param name="fileName">the filename for the new stack level
    /// </param>
    /// <param name="lineNumber">the line number at which the execution of the
    /// new stack level begins.
    /// </param>
    internal void pushDebugStack( string fileName, int lineNumber )
    {
      // do nothing.
    }

    /// <summary> Remove the top-most level of the debug stack.</summary>
    internal void popDebugStack()
    {
      // do nothing
    }
    /// <summary> Returns the line number where the given command argument begins. E.g, if
    /// the following command is at line 10:
    /// 
    /// foo {a
    /// b } c
    /// 
    /// getArgLine(0) = 10
    /// getArgLine(1) = 10
    /// getArgLine(2) = 11
    /// 
    /// </summary>
    /// <param name="index">specifies an argument.
    /// </param>
    /// <returns> the line number of the given argument.
    /// </returns>
    internal int getArgLineNumber( int index )
    {
      return 0;
    }
    internal void transferResult( Interp sourceInterp, TCL.CompletionCode result )
    {
      if ( sourceInterp == this )
      {
        return;
      }

      if ( result == TCL.CompletionCode.ERROR )
      {
        TclObject obj;

        // An error occurred, so transfer error information from the source
        // interpreter to the target interpreter.  Setting the flags tells
        // the target interp that it has inherited a partial traceback
        // chain, not just a simple error message.

        if ( !sourceInterp.errAlreadyLogged )
        {
          sourceInterp.addErrorInfo( "" );
        }
        sourceInterp.errAlreadyLogged = true;

        resetResult();

        obj = sourceInterp.getVar( "errorInfo", TCL.VarFlag.GLOBAL_ONLY );
        setVar( "errorInfo", obj, TCL.VarFlag.GLOBAL_ONLY );

        obj = sourceInterp.getVar( "errorCode", TCL.VarFlag.GLOBAL_ONLY );
        setVar( "errorCode", obj, TCL.VarFlag.GLOBAL_ONLY );

        errInProgress = true;
        errCodeSet = true;
      }

      returnCode = result;
      setResult( sourceInterp.getResult() );
      sourceInterp.resetResult();

      if ( result != TCL.CompletionCode.OK )
      {

        throw new TclException( this, getResult().ToString(), result );
      }
    }
    internal void hideCommand( string cmdName, string hiddenCmdToken )
    {
      WrappedCommand cmd;

      if ( deleted )
      {
        // The interpreter is being deleted. Do not create any new
        // structures, because it is not safe to modify the interpreter.
        return;
      }

      // Disallow hiding of commands that are currently in a namespace or
      // renaming (as part of hiding) into a namespace.
      //
      // (because the current implementation with a single global table
      //  and the needed uniqueness of names cause problems with namespaces)
      //
      // we don't need to check for "::" in cmdName because the real check is
      // on the nsPtr below.
      //
      // hiddenCmdToken is just a string which is not interpreted in any way.
      // It may contain :: but the string is not interpreted as a namespace
      // qualifier command name. Thus, hiding foo::bar to foo::bar and then
      // trying to expose or invoke ::foo::bar will NOT work; but if the
      // application always uses the same strings it will get consistent
      // behavior.
      //
      // But as we currently limit ourselves to the global namespace only
      // for the source, in order to avoid potential confusion,
      // lets prevent "::" in the token too.  --dl

      if ( hiddenCmdToken.IndexOf( "::" ) >= 0 )
      {
        throw new TclException( this, "cannot use namespace qualifiers as " + "hidden commandtoken (rename)" );
      }

      // Find the command to hide. An error is returned if cmdName can't
      // be found. Look up the command only from the global namespace.
      // Full path of the command must be given if using namespaces.

      cmd = NamespaceCmd.findCommand( this, cmdName, null, TCL.VarFlag.LEAVE_ERR_MSG | TCL.VarFlag.GLOBAL_ONLY );

      // Check that the command is really in global namespace

      if ( cmd.ns != globalNs )
      {
        throw new TclException( this, "can only hide global namespace commands" + " (use rename then hide)" );
      }

      // Initialize the hidden command table if necessary.

      if ( hiddenCmdTable == null )
      {
        hiddenCmdTable = new Hashtable();
      }

      // It is an error to move an exposed command to a hidden command with
      // hiddenCmdToken if a hidden command with the name hiddenCmdToken already
      // exists.

      if ( hiddenCmdTable.ContainsKey( hiddenCmdToken ) )
      {
        throw new TclException( this, "hidden command named \"" + hiddenCmdToken + "\" already exists" );
      }

      // Nb : This code is currently 'like' a rename to a specialy set apart
      // name table. Changes here and in TclRenameCommand must
      // be kept in synch untill the common parts are actually
      // factorized out.

      // Remove the hash entry for the command from the interpreter command
      // table. This is like deleting the command, so bump its command epoch;
      // this invalidates any cached references that point to the command.

      if ( cmd.table.ContainsKey( cmd.hashKey ) )
      {
        SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
      }

      // Now link the hash table entry with the command structure.
      // We ensured above that the nsPtr was right.

      cmd.table = hiddenCmdTable;
      cmd.hashKey = hiddenCmdToken;
      SupportClass.PutElement( hiddenCmdTable, hiddenCmdToken, cmd );
    }
    internal void exposeCommand( string hiddenCmdToken, string cmdName )
    {
      WrappedCommand cmd;

      if ( deleted )
      {
        // The interpreter is being deleted. Do not create any new
        // structures, because it is not safe to modify the interpreter.
        return;
      }

      // Check that we have a regular name for the command
      // (that the user is not trying to do an expose and a rename
      //  (to another namespace) at the same time)

      if ( cmdName.IndexOf( "::" ) >= 0 )
      {
        throw new TclException( this, "can not expose to a namespace " + "(use expose to toplevel, then rename)" );
      }

      // Get the command from the hidden command table:

      if ( hiddenCmdTable == null || !hiddenCmdTable.ContainsKey( hiddenCmdToken ) )
      {
        throw new TclException( this, "unknown hidden command \"" + hiddenCmdToken + "\"" );
      }
      cmd = (WrappedCommand)hiddenCmdTable[hiddenCmdToken];

      // Check that we have a true global namespace
      // command (enforced by Tcl_HideCommand() but let's double
      // check. (If it was not, we would not really know how to
      // handle it).

      if ( cmd.ns != globalNs )
      {

        // This case is theoritically impossible,
        // we might rather panic() than 'nicely' erroring out ?

        throw new TclException( this, "trying to expose " + "a non global command name space command" );
      }

      // This is the global table
      NamespaceCmd.Namespace ns = cmd.ns;

      // It is an error to overwrite an existing exposed command as a result
      // of exposing a previously hidden command.

      if ( ns.cmdTable.ContainsKey( cmdName ) )
      {
        throw new TclException( this, "exposed command \"" + cmdName + "\" already exists" );
      }

      // Remove the hash entry for the command from the interpreter hidden
      // command table.

      if ( (System.Object)cmd.hashKey != null )
      {
        SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
        cmd.table = ns.cmdTable;
        cmd.hashKey = cmdName;
      }

      // Now link the hash table entry with the command structure.
      // This is like creating a new command, so deal with any shadowing
      // of commands in the global namespace.

      ns.cmdTable.Add( cmdName, cmd );

      // Not needed as we are only in the global namespace
      // (but would be needed again if we supported namespace command hiding)

      // TclResetShadowedCmdRefs(interp, cmdPtr);
    }
    internal void hideUnsafeCommands()
    {
      for ( int ix = 0; ix < unsafeCmds.Length; ix++ )
      {
        try
        {
          hideCommand( unsafeCmds[ix], unsafeCmds[ix] );
        }
        catch ( TclException e )
        {
          if ( !e.Message.StartsWith( "unknown command" ) )
          {
            throw;
          }
        }
      }
    }
    internal TCL.CompletionCode invokeGlobal( TclObject[] objv, int flags )
    {
      CallFrame savedVarFrame = varFrame;

      try
      {
        varFrame = null;
        return invoke( objv, flags );
      }
      finally
      {
        varFrame = savedVarFrame;
      }
    }
    internal TCL.CompletionCode invoke( TclObject[] objv, int flags )
    {
      if ( ( objv.Length < 1 ) || ( objv == null ) )
      {
        throw new TclException( this, "illegal argument vector" );
      }


      string cmdName = objv[0].ToString();
      WrappedCommand cmd;
      TclObject[] localObjv = null;

      if ( ( flags & INVOKE_HIDDEN ) != 0 )
      {

        // We never invoke "unknown" for hidden commands.

        if ( hiddenCmdTable == null || !hiddenCmdTable.ContainsKey( cmdName ) )
        {
          throw new TclException( this, "invalid hidden command name \"" + cmdName + "\"" );
        }
        cmd = (WrappedCommand)hiddenCmdTable[cmdName];
      }
      else
      {
        cmd = NamespaceCmd.findCommand( this, cmdName, null, TCL.VarFlag.GLOBAL_ONLY );
        if ( cmd == null )
        {
          if ( ( flags & INVOKE_NO_UNKNOWN ) == 0 )
          {
            cmd = NamespaceCmd.findCommand( this, "unknown", null, TCL.VarFlag.GLOBAL_ONLY );
            if ( cmd != null )
            {
              localObjv = new TclObject[objv.Length + 1];
              localObjv[0] = TclString.newInstance( "unknown" );
              localObjv[0].preserve();
              for ( int i = 0; i < objv.Length; i++ )
              {
                localObjv[i + 1] = objv[i];
              }
              objv = localObjv;
            }
          }

          // Check again if we found the command. If not, "unknown" is
          // not present and we cannot help, or the caller said not to
          // call "unknown" (they specified TCL_INVOKE_NO_UNKNOWN).

          if ( cmd == null )
          {
            throw new TclException( this, "invalid command name \"" + cmdName + "\"" );
          }
        }
      }

      // Invoke the command procedure. First reset the interpreter's string
      // and object results to their default empty values since they could
      // have gotten changed by earlier invocations.

      resetResult();
      cmdCount++;

      TCL.CompletionCode result = TCL.CompletionCode.OK;
      try
      {
        cmd.cmd.cmdProc( this, objv );
      }
      catch ( TclException e )
      {
        result = e.getCompletionCode();
      }

      // If we invoke a procedure, which was implemented as AutoloadStub,
      // it was entered into the ordinary cmdTable. But here we know
      // for sure, that this command belongs into the hiddenCmdTable.
      // So if we can find an entry in cmdTable with the cmdName, just
      // move it into the hiddenCmdTable.

      if ( ( flags & INVOKE_HIDDEN ) != 0 )
      {
        cmd = NamespaceCmd.findCommand( this, cmdName, null, TCL.VarFlag.GLOBAL_ONLY );
        if ( cmd != null )
        {
          // Basically just do the same as in hideCommand...
          SupportClass.HashtableRemove( cmd.table, cmd.hashKey );
          cmd.table = hiddenCmdTable;
          cmd.hashKey = cmdName;
          SupportClass.PutElement( hiddenCmdTable, cmdName, cmd );
        }
      }

      // If an error occurred, record information about what was being
      // executed when the error occurred.

      if ( ( result == TCL.CompletionCode.ERROR ) && ( ( flags & INVOKE_NO_TRACEBACK ) == 0 ) && !errAlreadyLogged )
      {
        StringBuilder ds;

        if ( errInProgress )
        {
          ds = new StringBuilder( "\n    while invoking\n\"" );
        }
        else
        {
          ds = new StringBuilder( "\n    invoked from within\n\"" );
        }
        for ( int i = 0; i < objv.Length; i++ )
        {

          ds.Append( objv[i].ToString() );
          if ( i < ( objv.Length - 1 ) )
          {
            ds.Append( " " );
          }
          else if ( ds.Length > 100 )
          {
            ds.Append( "..." );
            break;
          }
        }
        ds.Append( "\"" );
        addErrorInfo( ds.ToString() );
        errInProgress = true;
      }

      // Free any locally allocated storage used to call "unknown".

      if ( localObjv != null )
      {
        localObjv[0].release();
      }

      return result;
    }
    internal void allowExceptions()
    {
      evalFlags |= Parser.TCL_ALLOW_EXCEPTIONS;
    }

    internal class ResolverScheme
    {
      private void InitBlock( Interp enclosingInstance )
      {
        this.enclosingInstance = enclosingInstance;
      }
      private Interp enclosingInstance;
      public Interp Enclosing_Instance
      {
        get
        {
          return enclosingInstance;
        }

      }

      internal string name; // Name identifying this scheme.
      internal Resolver resolver;

      internal ResolverScheme( Interp enclosingInstance, string name, Resolver resolver )
      {
        InitBlock( enclosingInstance );
        this.name = name;
        this.resolver = resolver;
      }
    }

    public void addInterpResolver( string name, Resolver resolver )
    // Object to resolve commands/variables.
    {
      IEnumerator enum_Renamed;
      ResolverScheme res;

      //  Look for an existing scheme with the given name.
      //  If found, then replace its rules.

      if ( resolvers != null )
      {
        for ( enum_Renamed = resolvers.GetEnumerator(); enum_Renamed.MoveNext(); )
        {
          res = (ResolverScheme)enum_Renamed.Current;
          if ( name.Equals( res.name ) )
          {
            res.resolver = resolver;
            return;
          }
        }
      }

      if ( resolvers == null )
      {
        resolvers = new ArrayList( 10 );
      }

      //  Otherwise, this is a new scheme.  Add it to the FRONT
      //  of the linked list, so that it overrides existing schemes.

      res = new ResolverScheme( this, name, resolver );

      resolvers.Insert( 0, res );
    }
    public Resolver getInterpResolver( string name )
    // Look for a scheme with this name.
    {
      //IEnumerator enum;

      //  Look for an existing scheme with the given name.  If found,
      //  then return pointers to its procedures.

      if ( resolvers != null )
      {
        foreach ( ResolverScheme res in resolvers )
        {
          if ( name.Equals( res.name ) )
          {
            return res.resolver;
          }
        }
      }

      return null;
    }
    internal bool removeInterpResolver( string name )
    // Name of the scheme to be removed.
    {
      ResolverScheme res;
      IEnumerator enum_Renamed;
      bool found = false;

      //  Look for an existing scheme with the given name.

      if ( resolvers != null )
      {
        enum_Renamed = resolvers.GetEnumerator();
        while ( !found && enum_Renamed.MoveNext() )
        {
          res = (ResolverScheme)enum_Renamed.Current;
          if ( name.Equals( res.name ) )
          {
            found = true;
          }
        }
      }

      //  If we found the scheme, delete it.

      if ( found )
      {
        SupportClass.VectorRemoveElement( resolvers, name );
      }

      return found;
    }

  } // end Interp
}