internal static ParseResult parseNestedCmd( Interp interp, string inString, int index, int length ) { CharPointer script; TclObject obj; // Check for the easy case where the last character in the string is '['. if ( index == length ) { throw new TclException( interp, "missing close-bracket" ); } script = new CharPointer( inString ); script.index = index; interp.evalFlags |= Parser.TCL_BRACKET_TERM; Parser.eval2( interp, script.array, script.index, length - index, 0 ); obj = interp.getResult(); obj.preserve(); return ( new ParseResult( obj, index + interp.termOffset + 1 ) ); }
/// <summary> This procedure is invoked to process the "catch" Tcl command. /// See the user documentation for details on what it does. /// /// </summary> /// <param name="interp">the current interpreter. /// </param> /// <param name="argv">command arguments. /// </param> /// <exception cref=""> TclException if wrong number of arguments. /// </exception> public TCL.CompletionCode cmdProc(Interp interp, TclObject[] argv) { if (argv.Length != 2 && argv.Length != 3) { throw new TclNumArgsException(interp, 1, argv, "command ?varName?"); } TclObject result; TCL.CompletionCode code = TCL.CompletionCode.OK; try { interp.eval(argv[1], 0); } catch (TclException e) { code = e.getCompletionCode(); } result = interp.getResult(); if ( argv.Length == 3 ) { try { interp.setVar(argv[2], result, 0); } catch (TclException e) { throw new TclException(interp, "couldn't save command result in variable"); } } interp.resetResult(); interp.setResult(TclInteger.newInstance((int)code)); return TCL.CompletionCode.RETURN; }
/// <summary> This procedure is invoked to process the "subst" Tcl command. /// See the user documentation for details on what it does. /// /// </summary> /// <param name="interp">the current interpreter. /// </param> /// <param name="argv">command arguments. /// </param> /// <exception cref=""> TclException if wrong # of args or invalid argument(s). /// </exception> public TCL.CompletionCode cmdProc( Interp interp, TclObject[] argv ) { int currentObjIndex, len, i; int objc = argv.Length - 1; bool doBackslashes = true; bool doCmds = true; bool doVars = true; StringBuilder result = new StringBuilder(); string s; char c; for ( currentObjIndex = 1; currentObjIndex < objc; currentObjIndex++ ) { if ( !argv[currentObjIndex].ToString().StartsWith( "-" ) ) { break; } int opt = TclIndex.get( interp, argv[currentObjIndex], validCmds, "switch", 0 ); switch ( opt ) { case OPT_NOBACKSLASHES: doBackslashes = false; break; case OPT_NOCOMMANDS: doCmds = false; break; case OPT_NOVARS: doVars = false; break; default: throw new TclException( interp, "SubstCmd.cmdProc: bad option " + opt + " index to cmds" ); } } if ( currentObjIndex != objc ) { throw new TclNumArgsException( interp, currentObjIndex, argv, "?-nobackslashes? ?-nocommands? ?-novariables? string" ); } /* * Scan through the string one character at a time, performing * command, variable, and backslash substitutions. */ s = argv[currentObjIndex].ToString(); len = s.Length; i = 0; while ( i < len ) { c = s[i]; if ( ( c == '[' ) && doCmds ) { ParseResult res; try { interp.evalFlags = Parser.TCL_BRACKET_TERM; interp.eval( s.Substring( i + 1, ( len ) - ( i + 1 ) ) ); TclObject interp_result = interp.getResult(); interp_result.preserve(); res = new ParseResult( interp_result, i + interp.termOffset ); } catch ( TclException e ) { i = e.errIndex + 1; throw; } i = res.nextIndex + 2; result.Append( res.value.ToString() ); res.release(); } else if ( c == '\r' ) { /* * (ToDo) may not be portable on Mac */ i++; } else if ( ( c == '$' ) && doVars ) { ParseResult vres = Parser.parseVar( interp, s.Substring( i, ( len ) - ( i ) ) ); i += vres.nextIndex; result.Append( vres.value.ToString() ); vres.release(); } else if ( ( c == '\\' ) && doBackslashes ) { BackSlashResult bs = tcl.lang.Interp.backslash( s, i, len ); i = bs.nextIndex; if ( bs.isWordSep ) { break; } else { result.Append( bs.c ); } } else { result.Append( c ); i++; } } interp.setResult( result.ToString() ); return TCL.CompletionCode.RETURN; }
public static ParseResult parseVar( Interp interp, string inString ) { TclParse parse; TclObject obj; System.Diagnostics.Debug.WriteLine( "Entered parseVar()" ); System.Diagnostics.Debug.Write( "now to parse var off the string \"" + inString + "\"" ); CharPointer src = new CharPointer( inString ); parse = parseVarName( interp, src.array, src.index, -1, null, false ); if ( parse.result != TCL.CompletionCode.OK ) { throw new TclException( interp, interp.getResult().ToString() ); } try { System.Diagnostics.Debug.Write( "parsed " + parse.numTokens + " tokens" ); if ( parse.numTokens == 1 ) { // There isn't a variable name after all: the $ is just a $. return new ParseResult( "$", 1 ); } obj = evalTokens( interp, parse.tokenList, 0, parse.numTokens ); if ( !obj.Shared ) { throw new TclRuntimeError( "parseVar got temporary object from evalTokens" ); } return new ParseResult( obj, parse.tokenList[0].size ); } finally { parse.release(); // Release parser resources } }
internal static TclObject evalTokens( Interp interp, TclToken[] tokenList, int tIndex, int count ) { TclObject result, index, value; TclToken token; string p = null; string varName; BackSlashResult bs; // The only tricky thing about this procedure is that it attempts to // avoid object creation and string copying whenever possible. For // example, if the value is just a nested command, then use the // command's result object directly. result = null; for ( ; count > 0; count-- ) { token = tokenList[tIndex]; // The switch statement below computes the next value to be // concat to the result, as either a range of text or an // object. value = null; switch ( token.type ) { case TCL_TOKEN_TEXT: p = token.TokenString; break; case TCL_TOKEN_BS: bs = backslash( token.script_array, token.script_index ); if ( bs.isWordSep ) { p = "\\" + bs.c; } else { System.Char ch = bs.c; p = ch.ToString(); } break; case TCL_TOKEN_COMMAND: interp.evalFlags |= Parser.TCL_BRACKET_TERM; token.script_index++; //should the nest level be changed??? //interp.nestLevel++; eval2( interp, token.script_array, token.script_index, token.size - 2, 0 ); token.script_index--; //interp.nestLevel--; value = interp.getResult(); break; case TCL_TOKEN_VARIABLE: if ( token.numComponents == 1 ) { index = null; } else { index = evalTokens( interp, tokenList, tIndex + 2, token.numComponents - 1 ); if ( index == null ) { return null; } } varName = tokenList[tIndex + 1].TokenString; // In order to get the existing expr parser to work with the // new Parser, we test the interp.noEval flag which is set // by the expr parser. If it is != 0, then we do not evaluate // the variable. This should be removed when the new expr // parser is implemented. if ( interp.noEval == 0 ) { if ( index != null ) { try { value = interp.getVar( varName, index.ToString(), 0 ); } finally { index.release(); } } else { value = interp.getVar( varName, null, 0 ); } } else { value = TclString.newInstance( "" ); value.preserve(); } count -= token.numComponents; tIndex += token.numComponents; break; default: throw new TclRuntimeError( "unexpected token type in evalTokens" ); } // If value isn't null, the next piece of text comes from that // object; otherwise, take value of p. if ( result == null ) { if ( value != null ) { result = value; } else { result = TclString.newInstance( p ); } result.preserve(); } else { if ( result.Shared ) { result.release(); result = result.duplicate(); result.preserve(); } if ( value != null ) { p = value.ToString(); } TclString.append( result, p ); } tIndex++; } return result; }
/// <summary> CallTraces -> callTraces /// /// This procedure is invoked to find and invoke relevant /// trace procedures associated with a particular operation on /// a variable. This procedure invokes traces both on the /// variable and on its containing array (where relevant). /// /// </summary> /// <param name="interp">Interpreter containing variable. /// </param> /// <param name="array">array variable that contains the variable, or null /// if the variable isn't an element of an array. /// </param> /// <param name="var">Variable whose traces are to be invoked. /// </param> /// <param name="part1">the first part of a variable name. /// </param> /// <param name="part2">the second part of a variable name. /// </param> /// <param name="flags">Flags to pass to trace procedures: indicates /// what's happening to variable, plus other stuff like /// TCL.VarFlag.GLOBAL_ONLY, TCL.VarFlag.NAMESPACE_ONLY, and TCL.VarFlag.INTERP_DESTROYED. /// </param> /// <returns> null if no trace procedures were invoked, or /// if all the invoked trace procedures returned successfully. /// The return value is non-null if a trace procedure returned an /// error (in this case no more trace procedures were invoked /// after the error was returned). In this case the return value /// is a pointer to a string describing the error. /// </returns> static protected internal string callTraces( Interp interp, Var array, Var var, string part1, string part2, TCL.VarFlag flags ) { TclObject oldResult; int i; // If there are already similar trace procedures active for the // variable, don't call them again. if ( ( var.flags & VarFlags.TRACE_ACTIVE ) != 0 ) { return null; } var.flags |= VarFlags.TRACE_ACTIVE; var.refCount++; // If the variable name hasn't been parsed into array name and // element, do it here. If there really is an array element, // make a copy of the original name so that nulls can be // inserted into it to separate the names (can't modify the name // string in place, because the string might get used by the // callbacks we invoke). // FIXME : come up with parsing code to use for all situations! if ( (System.Object)part2 == null ) { int len = part1.Length; if ( len > 0 ) { if ( part1[len - 1] == ')' ) { for ( i = 0; i < len - 1; i++ ) { if ( part1[i] == '(' ) { break; } } if ( i < len - 1 ) { if ( i < len - 2 ) { part2 = part1.Substring( i + 1, ( len - 1 ) - ( i + 1 ) ); part1 = part1.Substring( 0, ( i ) - ( 0 ) ); } } } } } oldResult = interp.getResult(); oldResult.preserve(); interp.resetResult(); try { // Invoke traces on the array containing the variable, if relevant. if ( array != null ) { array.refCount++; } if ( ( array != null ) && ( array.traces != null ) ) { for ( i = 0; ( array.traces != null ) && ( i < array.traces.Count ); i++ ) { TraceRecord rec = (TraceRecord)array.traces[i]; if ( ( rec.flags & flags ) != 0 ) { try { rec.trace.traceProc( interp, part1, part2, flags ); } catch ( TclException e ) { if ( ( flags & TCL.VarFlag.TRACE_UNSETS ) == 0 ) { return interp.getResult().ToString(); } } } } } // Invoke traces on the variable itself. if ( ( flags & TCL.VarFlag.TRACE_UNSETS ) != 0 ) { flags |= TCL.VarFlag.TRACE_DESTROYED; } for ( i = 0; ( var.traces != null ) && ( i < var.traces.Count ); i++ ) { TraceRecord rec = (TraceRecord)var.traces[i]; if ( ( rec.flags & flags ) != 0 ) { try { rec.trace.traceProc( interp, part1, part2, flags ); } catch ( TclException e ) { if ( ( flags & TCL.VarFlag.TRACE_UNSETS ) == 0 ) { return interp.getResult().ToString(); } } } } return null; } finally { if ( array != null ) { array.refCount--; } var.flags &= ~VarFlags.TRACE_ACTIVE; var.refCount--; interp.setResult( oldResult ); oldResult.release(); } }
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 ); } }
/* *---------------------------------------------------------------------- * * NamespaceEvalCmd -> evalCmd * * Invoked to implement the "namespace eval" command. Executes * commands in a namespace. If the namespace does not already exist, * it is created. Handles the following syntax: * * namespace eval name arg ?arg...? * * If more than one arg argument is specified, the command that is * executed is the result of concatenating the arguments together with * a space between each argument. * * Results: * Returns if successful, raises TclException if something goes wrong. * * Side effects: * Returns the result of the command in the interpreter's result * object. If anything goes wrong, this procedure returns an error * message as the result. * *---------------------------------------------------------------------- */ private static void evalCmd(Interp interp, TclObject[] objv) { Namespace namespace_Renamed; CallFrame frame; string cmd; string name; int length; if (objv.Length < 4) { throw new TclNumArgsException(interp, 2, objv, "name arg ?arg...?"); } // Try to resolve the namespace reference, caching the result in the // namespace object along the way. namespace_Renamed = getNamespaceFromObj(interp, objv[2]); // If the namespace wasn't found, try to create it. if (namespace_Renamed == null) { name = objv[2].ToString(); namespace_Renamed = createNamespace(interp, name, null); if (namespace_Renamed == null) { // FIXME : result hack, we get the interp result and throw it! throw new TclException(interp, interp.getResult().ToString()); } } // Make the specified namespace the current namespace and evaluate // the command(s). frame = interp.newCallFrame(); pushCallFrame(interp, frame, namespace_Renamed, false); try { if (objv.Length == 4) { interp.eval(objv[3], 0); } else { cmd = Util.concat(3, objv.Length, objv); // eval() will delete the object when it decrements its // refcount after eval'ing it. interp.eval(cmd); // do not pass TCL_EVAL_DIRECT, for compiler only } } catch (TclException ex) { if (ex.getCompletionCode() == TCL.CompletionCode.ERROR) { interp.addErrorInfo("\n (in namespace eval \"" + namespace_Renamed.fullName + "\" script line " + interp.errorLine + ")"); } throw ex; } finally { popCallFrame(interp); } return ; }
/* ** 2009 July 17 ** ** The author disclaims copyright to this source code. In place of ** a legal notice, here is a blessing: ** ** May you do good and not evil. ** May you find forgiveness for yourself and forgive others. ** May you share freely, never taking more than you give. ** ************************************************************************* ** This file contains code to implement the "sqlite" test harness ** which runs TCL commands for testing the C#-SQLite port. ** ** $Header$ */ public static void Main(string[] args) { // Array of command-line argument strings. { string fileName = null; // Create the interpreter. This will also create the built-in // Tcl commands. Interp interp = new Interp(); // Make command-line arguments available in the Tcl variables "argc" // and "argv". If the first argument doesn't start with a "-" then // strip it off and use it as the name of a script file to process. // We also set the argv0 and TCL.Tcl_interactive vars here. if ((args.Length > 0) && !(args[0].StartsWith("-"))) { fileName = args[0]; } TclObject argv = TclList.newInstance(); argv.preserve(); try { int i = 0; int argc = args.Length; if ((System.Object)fileName == null) { interp.setVar("argv0", "tcl.lang.Shell", TCL.VarFlag.GLOBAL_ONLY); interp.setVar("tcl_interactive", "1", TCL.VarFlag.GLOBAL_ONLY); } else { interp.setVar("argv0", fileName, TCL.VarFlag.GLOBAL_ONLY); interp.setVar("tcl_interactive", "0", TCL.VarFlag.GLOBAL_ONLY); i++; argc--; } for (; i < args.Length; i++) { TclList.append(interp, argv, TclString.newInstance(args[i])); } interp.setVar("argv", argv, TCL.VarFlag.GLOBAL_ONLY); interp.setVar("argc", System.Convert.ToString(argc), TCL.VarFlag.GLOBAL_ONLY); } catch (TclException e) { throw new TclRuntimeError("unexpected TclException: " + e.Message); } finally { argv.release(); } // Normally we would do application specific initialization here. // However, that feature is not currently supported. // If a script file was specified then just source that file // and quit. Console.WriteLine("C#-TCL version " + Assembly.GetExecutingAssembly().GetName().Version.ToString()); Console.WriteLine("=============================================================="); Console.WriteLine(""); if ((System.Object)fileName != null) { try { interp.evalFile(fileName); } catch (TclException e) { TCL.CompletionCode code = e.getCompletionCode(); if (code == TCL.CompletionCode.RETURN) { code = interp.updateReturnInfo(); if (code != TCL.CompletionCode.OK) { System.Console.Error.WriteLine("command returned bad code: " + code); if (tcl.lang.ConsoleThread.debug) System.Diagnostics.Debug.WriteLine("command returned bad code: " + code); } } else if (code == TCL.CompletionCode.ERROR) { System.Console.Error.WriteLine(interp.getResult().ToString()); if (tcl.lang.ConsoleThread.debug) System.Diagnostics.Debug.WriteLine(interp.getResult().ToString()); System.Diagnostics.Debug.Assert(false, interp.getResult().ToString()); } else { System.Console.Error.WriteLine("command returned bad code: " + code); if (tcl.lang.ConsoleThread.debug) System.Diagnostics.Debug.WriteLine("command returned bad code: " + code); } } // Note that if the above interp.evalFile() returns the main // thread will exit. This may bring down the VM and stop // the execution of Tcl. // // If the script needs to handle events, it must call // vwait or do something similar. // // Note that the script can create AWT widgets. This will // start an AWT event handling thread and keep the VM up. However, // the interpreter thread (the same as the main thread) would // have exited and no Tcl scripts can be executed. interp.dispose(); System.Environment.Exit(0); } if ((System.Object)fileName == null) { // We are running in interactive mode. Start the ConsoleThread // that loops, grabbing stdin and passing it to the interp. ConsoleThread consoleThread = new ConsoleThread(interp); consoleThread.IsBackground = true; consoleThread.Start(); // Loop forever to handle user input events in the command line. Notifier notifier = interp.getNotifier(); while (true) { // process events until "exit" is called. notifier.doOneEvent(TCL.ALL_EVENTS); } } } }