interp.cc

Go to the documentation of this file.
00001 
00003 
00004 //
00005 // Copyright (c) 2000-2004 California Institute of Technology
00006 // Copyright (c) 2004-2007 University of Southern California
00007 // Rob Peters <rjpeters at usc dot edu>
00008 //
00009 // created: Wed Oct 11 10:27:35 2000
00010 // commit: $Id: interp.cc 10065 2007-04-12 05:54:56Z rjpeters $
00011 // $HeadURL: file:///lab/rjpeters/svnrepo/code/trunk/groovx/src/tcl/interp.cc $
00012 //
00013 // --------------------------------------------------------------------
00014 //
00015 // This file is part of GroovX.
00016 //   [http://ilab.usc.edu/rjpeters/groovx/]
00017 //
00018 // GroovX is free software; you can redistribute it and/or modify it
00019 // under the terms of the GNU General Public License as published by
00020 // the Free Software Foundation; either version 2 of the License, or
00021 // (at your option) any later version.
00022 //
00023 // GroovX is distributed in the hope that it will be useful, but
00024 // WITHOUT ANY WARRANTY; without even the implied warranty of
00025 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00026 // General Public License for more details.
00027 //
00028 // You should have received a copy of the GNU General Public License
00029 // along with GroovX; if not, write to the Free Software Foundation,
00030 // Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00031 //
00033 
00034 #ifndef GROOVX_TCL_INTERP_CC_UTC20050628162421_DEFINED
00035 #define GROOVX_TCL_INTERP_CC_UTC20050628162421_DEFINED
00036 
00037 #include "tcl/interp.h"
00038 
00039 #include "rutz/demangle.h"
00040 #include "rutz/error.h"
00041 #include "rutz/fstring.h"
00042 #include "rutz/sfmt.h"
00043 
00044 #include "tcl/list.h" // for eval_objv()
00045 
00046 #include <exception>
00047 #include <tcl.h>
00048 #include <typeinfo>
00049 
00050 #include "rutz/trace.h"
00051 #include "rutz/debug.h"
00052 GVX_DBG_REGISTER
00053 
00054 using rutz::fstring;
00055 
00056 namespace
00057 {
00058   void c_interp_delete_proc(void* clientdata, Tcl_Interp*) throw()
00059   {
00060     tcl::interpreter* intp = static_cast<tcl::interpreter*>(clientdata);
00061     intp->forget_interp();
00062   }
00063 
00064   bool report_error(tcl::interpreter& interp, const tcl::obj& code,
00065                     tcl::error_strategy strategy,
00066                     const rutz::file_pos& where)
00067   {
00068     switch (strategy)
00069       {
00070       case tcl::THROW_ERROR:
00071         {
00072           const fstring msg =
00073             rutz::sfmt("error while evaluating %s:\n%s",
00074                        Tcl_GetString(code.get()),
00075                        interp.get_result<const char*>());
00076 
00077           // Now clear the interpreter's result string, since we've
00078           // already incorporated that message into our error message:
00079           interp.reset_result();
00080 
00081           throw rutz::error(msg, where);
00082         }
00083         break;
00084       case tcl::IGNORE_ERROR:
00085         return false;
00086       }
00087 
00088     return false;
00089   }
00090 }
00091 
00093 //
00094 // tcl::interpreter member definitions
00095 //
00097 
00098 tcl::interpreter::interpreter(Tcl_Interp* interp) :
00099   m_interp(interp)
00100 {
00101 GVX_TRACE("tcl::interpreter::interpreter");
00102   if (interp == 0)
00103     throw rutz::error("tried to make tcl::interpreter "
00104                       "with a null Tcl_Interp*", SRC_POS);
00105 
00106   Tcl_CallWhenDeleted(m_interp, c_interp_delete_proc,
00107                       static_cast<void*>(this));
00108 }
00109 
00110 tcl::interpreter::interpreter(const tcl::interpreter& other) throw() :
00111   m_interp(other.m_interp)
00112 {
00113 GVX_TRACE("tcl::interpreter::interpreter(const interpreter&)");
00114 
00115   if (m_interp != 0)
00116     {
00117       Tcl_CallWhenDeleted(m_interp, c_interp_delete_proc,
00118                           static_cast<void*>(this));
00119     }
00120 }
00121 
00122 tcl::interpreter::~interpreter() throw()
00123 {
00124 GVX_TRACE("tcl::interpreter::~interpreter");
00125 
00126   if (m_interp != 0)
00127     Tcl_DontCallWhenDeleted(m_interp, c_interp_delete_proc,
00128                             static_cast<void*>(this));
00129 }
00130 
00132 //
00133 // tcl::interpreter -- Tcl_Interp management
00134 //
00136 
00137 Tcl_Interp* tcl::interpreter::intp() const
00138 {
00139   if (m_interp == 0)
00140     throw rutz::error("tcl::interpreter doesn't have a valid Tcl_Interp*",
00141                       SRC_POS);
00142 
00143   return m_interp;
00144 }
00145 
00146 bool tcl::interpreter::is_deleted() const throw()
00147 {
00148 GVX_TRACE("tcl::interpreter::is_deleted");
00149 
00150   return (m_interp == 0) || Tcl_InterpDeleted(m_interp);
00151 }
00152 
00153 void tcl::interpreter::forget_interp() throw()
00154 {
00155 GVX_TRACE("tcl::interpreter::forget_interp");
00156   m_interp = 0;
00157 }
00158 
00159 void tcl::interpreter::destroy() throw()
00160 {
00161 GVX_TRACE("tcl::interpreter::destroy");
00162 
00163   if (m_interp != 0)
00164     {
00165       Tcl_DeleteInterp(m_interp);
00166       m_interp = 0;
00167     }
00168 }
00169 
00171 //
00172 // tcl::interpreter -- Packages
00173 //
00175 
00176 void tcl::interpreter::pkg_provide(const char* name, const char* version)
00177 {
00178 GVX_TRACE("tcl::interpreter::pkg_provide");
00179   Tcl_PkgProvide(intp(), name, version);
00180 }
00181 
00183 //
00184 // tcl::interpreter -- Expressions
00185 //
00187 
00188 bool tcl::interpreter::eval_boolean_expr(const tcl::obj& obj) const
00189 {
00190 GVX_TRACE("tcl::interpreter::eval_boolean_expr");
00191 
00192   int expr_result;
00193 
00194   if (Tcl_ExprBooleanObj(intp(), obj.get(), &expr_result) != TCL_OK)
00195     {
00196       throw rutz::error("error evaluating boolean expression", SRC_POS);
00197     }
00198 
00199   return bool(expr_result);
00200 }
00201 
00203 //
00204 // tcl::interpreter -- Evaluating code
00205 //
00207 
00208 bool tcl::interpreter::eval(const char* code,
00209                             tcl::error_strategy strategy)
00210 {
00211   tcl::obj obj(tcl::convert_from(code));
00212   return eval(obj, strategy);
00213 }
00214 
00215 bool tcl::interpreter::eval(const fstring& code,
00216                             tcl::error_strategy strategy)
00217 {
00218   tcl::obj obj(tcl::convert_from(code));
00219   return eval(obj, strategy);
00220 }
00221 
00222 bool tcl::interpreter::eval(const tcl::obj& code,
00223                             tcl::error_strategy strategy)
00224 {
00225 GVX_TRACE("tcl::interpreter::eval");
00226 
00227   if (!is_valid())
00228     throw rutz::error("Tcl_Interp* was null "
00229                       "in tcl::interpreter::eval", SRC_POS);
00230 
00231   // We want to use TCL_EVAL_DIRECT here because that will avoid a
00232   // string conversion cycle -- that may be important if we have
00233   // objects with fragile representations (i.e., objects that can't
00234   // survive a object->string->object cycle because their string
00235   // representations don't represent the full object value).
00236 
00237   if ( Tcl_EvalObjEx(intp(), code.get(),
00238                      TCL_EVAL_DIRECT | TCL_EVAL_GLOBAL) == TCL_OK )
00239     return true;
00240 
00241   // else, there was some error during the Tcl eval...
00242 
00243   return report_error(*this, code, strategy, SRC_POS);
00244 }
00245 
00246 bool tcl::interpreter::eval_objv(const tcl::list& objv,
00247                                 tcl::error_strategy strategy)
00248 {
00249 GVX_TRACE("tcl::interpreter::eval_objv");
00250 
00251   if (!this->is_valid())
00252     throw rutz::error("Tcl_Interp* was null "
00253                       "in tcl::interpreter::eval", SRC_POS);
00254 
00255   if ( Tcl_EvalObjv(this->intp(), objv.length(), objv.elements(),
00256                     TCL_EVAL_GLOBAL) == TCL_OK )
00257     return true;
00258 
00259   // else, there was some error during the Tcl eval...
00260 
00261   return report_error(*this, objv.as_obj(), strategy, SRC_POS);
00262 }
00263 
00264 bool tcl::interpreter::eval_file(const char* fname)
00265 {
00266 GVX_TRACE("tcl::interpreter::eval_file");
00267   return (Tcl_EvalFile(intp(), fname) == TCL_OK);
00268 }
00269 
00270 void tcl::interpreter::source_rc_file()
00271 {
00272 GVX_TRACE("tcl::interpreter::source_rc_file");
00273   Tcl_SourceRCFile(intp());
00274 }
00275 
00277 //
00278 // tcl::interpreter -- Result
00279 //
00281 
00282 void tcl::interpreter::reset_result() const
00283 {
00284 GVX_TRACE("tcl::interpreter::reset_result");
00285 
00286   Tcl_ResetResult(intp());
00287 }
00288 
00289 void tcl::interpreter::append_result(const char* msg) const
00290 {
00291 GVX_TRACE("tcl::interpreter::append_result(const char*)");
00292 
00293   Tcl_AppendResult(intp(), msg, static_cast<char*>(0));
00294 }
00295 
00296 void tcl::interpreter::append_result(const fstring& msg) const
00297 {
00298 GVX_TRACE("tcl::interpreter::append_result(const fstring&)");
00299 
00300   Tcl_AppendResult(intp(), msg.c_str(), static_cast<char*>(0));
00301 }
00302 
00303 Tcl_Obj* tcl::interpreter::get_obj_result() const
00304 {
00305 GVX_TRACE("tcl::interpreter::get_obj_result");
00306 
00307   return Tcl_GetObjResult(intp());
00308 }
00309 
00310 void tcl::interpreter::set_obj_result(Tcl_Obj* obj)
00311 {
00312 GVX_TRACE("tcl::interpreter::set_obj_result");
00313 
00314   Tcl_SetObjResult(intp(), obj);
00315 }
00316 
00318 //
00319 // tcl::interpreter -- Variables
00320 //
00322 
00323 void tcl::interpreter::set_global_var(const char* var_name,
00324                                     const tcl::obj& var) const
00325 {
00326 GVX_TRACE("tcl::interpreter::set_global_var");
00327 
00328   if (Tcl_SetVar2Ex(intp(), const_cast<char*>(var_name), /*name2*/0,
00329                     var.get(), TCL_GLOBAL_ONLY) == 0)
00330     {
00331       throw rutz::error(rutz::sfmt("couldn't set global variable '%s'",
00332                                    var_name), SRC_POS);
00333     }
00334 }
00335 
00336 void tcl::interpreter::unset_global_var(const char* var_name) const
00337 {
00338 GVX_TRACE("tcl::interpreter::unset_global_var");
00339 
00340   if (Tcl_UnsetVar(intp(), const_cast<char*>(var_name),
00341                    TCL_GLOBAL_ONLY) != TCL_OK)
00342     {
00343       throw rutz::error(rutz::sfmt("couldn't unset global variable '%s'",
00344                                    var_name), SRC_POS);
00345     }
00346 }
00347 
00348 Tcl_Obj* tcl::interpreter::get_obj_global_var(const char* name1,
00349                                               const char* name2) const
00350 {
00351 GVX_TRACE("tcl::interpreter::get_obj_global_var");
00352   Tcl_Obj* obj = Tcl_GetVar2Ex(intp(),
00353                                const_cast<char*>(name1),
00354                                const_cast<char*>(name2),
00355                                TCL_GLOBAL_ONLY|TCL_LEAVE_ERR_MSG);
00356 
00357   if (obj == 0)
00358     {
00359       throw rutz::error(rutz::sfmt("couldn't get global variable '%s'",
00360                                    name1), SRC_POS);
00361     }
00362 
00363   return obj;
00364 }
00365 
00366 void tcl::interpreter::link_int(const char* var_name, int* addr,
00367                                 bool read_only)
00368 {
00369 GVX_TRACE("tcl::interpreter::link_int");
00370   dbg_eval_nl(3, var_name);
00371 
00372   int flag = TCL_LINK_INT;
00373   if (read_only) flag |= TCL_LINK_READ_ONLY;
00374 
00375   if ( Tcl_LinkVar(intp(), var_name,
00376                    reinterpret_cast<char *>(addr), flag)
00377        != TCL_OK )
00378     throw rutz::error("error while linking int variable", SRC_POS);
00379 }
00380 
00381 void tcl::interpreter::link_double(const char* var_name, double* addr,
00382                                    bool read_only)
00383 {
00384 GVX_TRACE("tcl::interpreter::link_double");
00385   dbg_eval_nl(3, var_name);
00386 
00387   int flag = TCL_LINK_DOUBLE;
00388   if (read_only) flag |= TCL_LINK_READ_ONLY;
00389 
00390   if ( Tcl_LinkVar(intp(), var_name,
00391                    reinterpret_cast<char *>(addr), flag)
00392        != TCL_OK )
00393     throw rutz::error("error while linking double variable", SRC_POS);
00394 }
00395 
00396 void tcl::interpreter::link_boolean(const char* var_name, int* addr,
00397                                     bool read_only)
00398 {
00399 GVX_TRACE("tcl::interpreter::link_boolean");
00400   dbg_eval_nl(3, var_name);
00401 
00402   int flag = TCL_LINK_BOOLEAN;
00403   if (read_only) flag |= TCL_LINK_READ_ONLY;
00404 
00405   if ( Tcl_LinkVar(intp(), var_name,
00406                    reinterpret_cast<char *>(addr), flag)
00407        != TCL_OK )
00408     throw rutz::error("error while linking boolean variable", SRC_POS);
00409 }
00410 
00411 void tcl::interpreter::handle_live_exception(const char* where,
00412                                              const rutz::file_pos& pos) throw()
00413 {
00414 GVX_TRACE("tcl::interpreter::handle_live_exception");
00415 
00416   try
00417     {
00418       throw;
00419     }
00420   catch (std::exception& err)
00421     {
00422       dbg_print_nl(3, "caught (std::exception&)");
00423 
00424       if (is_valid())
00425         {
00426           const char* what = err.what();
00427 
00428           const fstring msg =
00429             rutz::sfmt("%s caught at %s:%d:\n%s%s",
00430                        rutz::demangled_name(typeid(err)),
00431                        pos.m_file_name, pos.m_line_no,
00432                        ((where != 0 && where[0] != '\0')
00433                         ? rutz::sfmt("%s: ", where).c_str()
00434                         : ""),
00435                        ((what != 0 && what[0] != '\0')
00436                         ? rutz::sfmt("%s ", what).c_str()
00437                         : ""));
00438 
00439           append_result(msg);
00440         }
00441     }
00442   catch (...)
00443     {
00444       dbg_print_nl(3, "caught (...)");
00445 
00446       if (is_valid())
00447         {
00448           const fstring msg =
00449             rutz::sfmt("exception of unknown type caught at %s:%d\n%s",
00450                        pos.m_file_name, pos.m_line_no,
00451                        ((where != 0 && where[0] != '\0')
00452                         ? where
00453                         : ""));
00454 
00455           append_result(msg);
00456         }
00457     }
00458 }
00459 
00460 void tcl::interpreter::background_error() throw()
00461 {
00462 GVX_TRACE("tcl::interpreter::background_error");
00463 
00464   if (is_valid())
00465     Tcl_BackgroundError(m_interp);
00466 }
00467 
00468 void tcl::interpreter::add_error_info(const char* info)
00469 {
00470 GVX_TRACE("tcl::interpreter::add_error_info");
00471 
00472   Tcl_AddErrorInfo(intp(), info);
00473 }
00474 
00475 void tcl::interpreter::clear_event_queue()
00476 {
00477 GVX_TRACE("tcl::interpreter::clear_event_queue");
00478   while (Tcl_DoOneEvent(TCL_ALL_EVENTS|TCL_DONT_WAIT) != 0)
00479     { /* Empty loop body */ }
00480 }
00481 
00482 bool tcl::interpreter::has_command(const char* cmd_name) const
00483 {
00484 GVX_TRACE("tcl::interpreter::has_command");
00485   Tcl_CmdInfo info;
00486   int result = Tcl_GetCommandInfo(intp(), cmd_name, &info);
00487   return (result != 0);
00488 }
00489 
00490 void tcl::interpreter::delete_command(const char* cmd_name)
00491 {
00492 GVX_TRACE("tcl::interpreter::delete_command");
00493 
00494   // We must check if the interp has been tagged for deletion already,
00495   // since if it is then we must not attempt to use it to delete a Tcl
00496   // command (this results in "called Tcl_HashEntry on deleted
00497   // table"). Not deleting the command in that case does not cause a
00498   // resource leak, however, since the Tcl_Interp as part if its own
00499   // destruction will delete all commands associated with it.
00500   if ( !is_deleted() )
00501     {
00502       Tcl_DeleteCommand(intp(), cmd_name);
00503     }
00504 }
00505 
00506 fstring tcl::interpreter::get_proc_body(const char* proc_name)
00507 {
00508 GVX_TRACE("tcl::interpreter::get_proc_body");
00509   if (has_command(proc_name))
00510     {
00511       reset_result();
00512 
00513       if (eval(rutz::sfmt("info body %s", proc_name)))
00514         {
00515           fstring result = get_result<const char*>();
00516           reset_result();
00517           return result;
00518         }
00519     }
00520 
00521   return "";
00522 }
00523 
00524 void tcl::interpreter::create_proc(const char* namesp, const char* proc_name,
00525                                   const char* args, const char* body)
00526 {
00527 GVX_TRACE("tcl::interpreter::create_proc");
00528 
00529   if (namesp == 0 || (*namesp == '\0'))
00530     {
00531       namesp = "::";
00532     }
00533 
00534   const fstring proc_cmd =
00535     rutz::sfmt("namespace eval %s { proc %s {%s} {%s} }",
00536                namesp, proc_name, args ? args : "", body);
00537 
00538   eval(proc_cmd);
00539 }
00540 
00541 void tcl::interpreter::delete_proc(const char* namesp, const char* proc_name)
00542 {
00543 GVX_TRACE("tcl::interpreter::delete_proc");
00544 
00545   // by renaming to the empty string "", we delete the Tcl proc
00546   const fstring cmd_str =
00547     rutz::sfmt("rename %s::%s \"\"",
00548                ((namesp != 0) && (*namesp != '\0')) ? namesp : "",
00549                proc_name);
00550 
00551   eval(cmd_str);
00552 }
00553 
00554 static const char __attribute__((used)) vcid_groovx_tcl_interp_cc_utc20050628162421[] = "$Id: interp.cc 10065 2007-04-12 05:54:56Z rjpeters $ $HeadURL: file:
00555 #endif // !GROOVX_TCL_INTERP_CC_UTC20050628162421_DEFINED

The software described here is Copyright (c) 1998-2005, Rob Peters.
This page was generated Wed Dec 3 06:49:41 2008 by Doxygen version 1.5.5.