ChaiScript / ChaiScript
1
// This file is distributed under the BSD License.
2
// See "license.txt" for details.
3
// Copyright 2009-2012, Jonathan Turner (jonathan@emptycrate.com)
4
// Copyright 2009-2018, Jason Turner (jason@emptycrate.com)
5
// http://www.chaiscript.com
6

7
// This is an open source non-commercial project. Dear PVS-Studio, please check it.
8
// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
9

10

11
#ifndef CHAISCRIPT_ENGINE_HPP_
12
#define CHAISCRIPT_ENGINE_HPP_
13

14
#include <cassert>
15
#include <exception>
16
#include <fstream>
17
#include <functional>
18
#include <map>
19
#include <memory>
20
#include <mutex>
21
#include <set>
22
#include <stdexcept>
23
#include <vector>
24
#include <cstring>
25

26
#include "../chaiscript_defines.hpp"
27
#include "../chaiscript_threading.hpp"
28
#include "../dispatchkit/boxed_cast_helper.hpp"
29
#include "../dispatchkit/boxed_value.hpp"
30
#include "../dispatchkit/dispatchkit.hpp"
31
#include "../dispatchkit/type_conversions.hpp"
32
#include "../dispatchkit/proxy_functions.hpp"
33
#include "chaiscript_common.hpp"
34

35
#if defined(__linux__) || defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__)
36
#include <unistd.h>
37
#endif
38

39
#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__)
40
#include <dlfcn.h>
41
#endif
42

43
#if defined(CHAISCRIPT_NO_DYNLOAD)
44
#include "chaiscript_unknown.hpp"
45
#elif defined(CHAISCRIPT_WINDOWS)
46
#include "chaiscript_windows.hpp"
47
#elif _POSIX_VERSION
48
#include "chaiscript_posix.hpp"
49
#else
50
#include "chaiscript_unknown.hpp"
51
#endif
52

53

54
#include "../dispatchkit/exception_specification.hpp"
55

56
namespace chaiscript
57
{
58
   /// Namespace alias to provide cleaner and more explicit syntax to users.
59
   using Namespace = dispatch::Dynamic_Object;
60

61
  namespace detail
62
  {
63
    typedef std::shared_ptr<Loadable_Module> Loadable_Module_Ptr;
64
  }
65

66

67
  /// \brief The main object that the ChaiScript user will use.
68
  class ChaiScript_Basic {
69

70
    mutable chaiscript::detail::threading::shared_mutex m_mutex;
71
    mutable chaiscript::detail::threading::recursive_mutex m_use_mutex;
72

73
    std::set<std::string> m_used_files;
74
    std::map<std::string, detail::Loadable_Module_Ptr> m_loaded_modules;
75
    std::set<std::string> m_active_loaded_modules;
76

77
    std::vector<std::string> m_module_paths;
78
    std::vector<std::string> m_use_paths;
79

80
    std::unique_ptr<parser::ChaiScript_Parser_Base> m_parser;
81

82
    chaiscript::detail::Dispatch_Engine m_engine;
83

84
    std::map<std::string, std::function<Namespace&()>> m_namespace_generators;
85

86
    /// Evaluates the given string in by parsing it and running the results through the evaluator
87 1
    Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/  = false) 
88
    {
89
      try {
90 1
        const auto p = m_parser->parse(t_input, t_filename);
91 1
        return p->eval(chaiscript::detail::Dispatch_State(m_engine));
92
      }
93 1
      catch (chaiscript::eval::detail::Return_Value &rv) {
94 1
        return rv.retval;
95
      }
96
    }
97

98

99

100
    /// Evaluates the given file and looks in the 'use' paths
101 1
    Boxed_Value internal_eval_file(const std::string &t_filename) {
102 1
      for (const auto &path : m_use_paths)
103
      {
104
        try {
105 1
          const auto appendedpath = path + t_filename;
106 1
          return do_eval(load_file(appendedpath), appendedpath, true);
107 1
        } catch (const exception::file_not_found_error &) {
108
          // failed to load, try the next path
109 1
        } catch (const exception::eval_error &t_ee) {
110 1
          throw Boxed_Value(t_ee);
111
        }
112
      }
113

114
      // failed to load by any name
115 1
      throw exception::file_not_found_error(t_filename);
116

117
    }
118

119

120

121
    /// Evaluates the given string, used during eval() inside of a script
122 1
    Boxed_Value internal_eval(const std::string &t_e) {
123
      try {
124 1
        return do_eval(t_e, "__EVAL__", true);
125 1
      } catch (const exception::eval_error &t_ee) {
126 1
        throw Boxed_Value(t_ee);
127
      }
128
    }
129

130
    /// Returns the current evaluation m_engine
131 1
    chaiscript::detail::Dispatch_Engine &get_eval_engine() noexcept {
132 1
      return m_engine;
133
    }
134

135
    /// Builds all the requirements for ChaiScript, including its evaluator and a run of its prelude.
136 1
    void build_eval_system(const ModulePtr &t_lib, const std::vector<Options> &t_opts) {
137 1
      if (t_lib)
138
      {
139 1
        add(t_lib);
140
      }
141

142 1
      m_engine.add(fun([this](){ m_engine.dump_system(); }), "dump_system");
143 1
      m_engine.add(fun([this](const Boxed_Value &t_bv){ m_engine.dump_object(t_bv); }), "dump_object");
144 1
      m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_type){ return m_engine.is_type(t_bv, t_type); }), "is_type");
145 1
      m_engine.add(fun([this](const Boxed_Value &t_bv){ return m_engine.type_name(t_bv); }), "type_name");
146 1
      m_engine.add(fun([this](const std::string &t_f){ return m_engine.function_exists(t_f); }), "function_exists");
147 1
      m_engine.add(fun([this](){ return m_engine.get_function_objects(); }), "get_functions");
148 1
      m_engine.add(fun([this](){ return m_engine.get_scripting_objects(); }), "get_objects");
149

150 1
      m_engine.add(
151 1
          dispatch::make_dynamic_proxy_function(
152
              [this](const Function_Params &t_params) {
153
                return m_engine.call_exists(t_params);
154
              })
155
          , "call_exists");
156

157

158 1
      m_engine.add(fun(
159
            [this](const dispatch::Proxy_Function_Base &t_fun, const std::vector<Boxed_Value> &t_params) -> Boxed_Value {
160
              Type_Conversions_State s(this->m_engine.conversions(), this->m_engine.conversions().conversion_saves());
161
              return t_fun(Function_Params{t_params}, s);
162
            }), "call");
163

164

165 1
      m_engine.add(fun([this](const Type_Info &t_ti){ return m_engine.get_type_name(t_ti); }), "name");
166

167 1
      m_engine.add(fun([this](const std::string &t_type_name, bool t_throw){ return m_engine.get_type(t_type_name, t_throw); }), "type");
168 1
      m_engine.add(fun([this](const std::string &t_type_name){ return m_engine.get_type(t_type_name, true); }), "type");
169

170 1
      m_engine.add(fun(
171
            [this](const Type_Info &t_from, const Type_Info &t_to, const std::function<Boxed_Value (const Boxed_Value &)> &t_func) {
172
              m_engine.add(chaiscript::type_conversion(t_from, t_to, t_func));
173
            }
174
          ), "add_type_conversion");
175

176

177

178 1
      if (std::find(t_opts.begin(), t_opts.end(), Options::No_Load_Modules) == t_opts.end()
179 1
          && std::find(t_opts.begin(), t_opts.end(), Options::Load_Modules) != t_opts.end()) 
180
      {
181 1
        m_engine.add(fun([this](const std::string &t_module, const std::string &t_file){ return load_module(t_module, t_file); }), "load_module");
182 1
        m_engine.add(fun([this](const std::string &t_module){ return load_module(t_module); }), "load_module");
183
      }
184

185 1
      if (std::find(t_opts.begin(), t_opts.end(), Options::No_External_Scripts) == t_opts.end()
186 1
          && std::find(t_opts.begin(), t_opts.end(), Options::External_Scripts) != t_opts.end())
187
      {
188 1
        m_engine.add(fun([this](const std::string &t_file){ return use(t_file); }), "use");
189 1
        m_engine.add(fun([this](const std::string &t_file){ return internal_eval_file(t_file); }), "eval_file");
190
      }
191

192 1
      m_engine.add(fun([this](const std::string &t_str){ return internal_eval(t_str); }), "eval");
193 1
      m_engine.add(fun([this](const AST_Node &t_ast){ return eval(t_ast); }), "eval");
194

195 1
      m_engine.add(fun([this](const std::string &t_str, const bool t_dump){ return parse(t_str, t_dump); }), "parse");
196 1
      m_engine.add(fun([this](const std::string &t_str){ return parse(t_str); }), "parse");
197

198

199 1
      m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global_const(t_bv, t_name); }), "add_global_const");
200 1
      m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global");
201 1
      m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global");
202

203
      // why this unused parameter to Namespace?
204 1
      m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace([](Namespace& /*space*/) {}, t_namespace_name); import(t_namespace_name); }), "namespace");
205 1
      m_engine.add(fun([this](const std::string& t_namespace_name) { import(t_namespace_name); }), "import");
206
    }
207

208
    /// Skip BOM at the beginning of file
209 1
    static bool skip_bom(std::ifstream &infile) {
210 1
        size_t bytes_needed = 3;
211
        char buffer[3];
212

213 1
        memset(buffer, '\0', bytes_needed);
214

215 1
        infile.read(buffer, static_cast<std::streamsize>(bytes_needed));
216

217 1
        if ((buffer[0] == '\xef')
218 1
            && (buffer[1] == '\xbb')
219 1
            && (buffer[2] == '\xbf')) {
220

221 1
            infile.seekg(3);
222 1
            return true;
223
        }
224

225 1
        infile.seekg(0);
226

227 1
        return false;
228
    }
229

230
    /// Helper function for loading a file
231 1
    static std::string load_file(const std::string &t_filename) {
232 1
      std::ifstream infile(t_filename.c_str(), std::ios::in | std::ios::ate | std::ios::binary );
233

234 1
      if (!infile.is_open()) {
235 1
        throw chaiscript::exception::file_not_found_error(t_filename);
236
      }
237

238 1
      auto size = infile.tellg();
239 1
      infile.seekg(0, std::ios::beg);
240

241 0
      assert(size >= 0);
242

243 1
      if (skip_bom(infile)) {
244 1
          size-=3; // decrement the BOM size from file size, otherwise we'll get parsing errors
245 0
          assert(size >= 0); //and check if there's more text
246
      }
247

248 1
      if (size == std::streampos(0))
249
      {
250 1
        return std::string();
251
      } else {
252 1
        std::vector<char> v(static_cast<size_t>(size));
253 1
        infile.read(&v[0], static_cast<std::streamsize>(size));
254 1
        return std::string(v.begin(), v.end());
255
      }
256
    }
257

258 1
    std::vector<std::string> ensure_minimum_path_vec(std::vector<std::string> paths)
259
    {
260 1
      if (paths.empty()) { return {""}; }
261 1
      else { return paths; }
262
    }
263

264
  public:
265

266
    /// \brief Constructor for ChaiScript
267
    /// \param[in] t_lib Standard library to apply to this ChaiScript instance
268
    /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module
269
    /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file
270 1
    ChaiScript_Basic(const ModulePtr &t_lib,
271
                     std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
272
                     std::vector<std::string> t_module_paths = {},
273
                     std::vector<std::string> t_use_paths = {},
274
                     const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options())
275 1
      : m_module_paths(ensure_minimum_path_vec(std::move(t_module_paths))),
276 1
        m_use_paths(ensure_minimum_path_vec(std::move(t_use_paths))),
277 1
        m_parser(std::move(parser)),
278 1
        m_engine(*m_parser)
279
    {
280
#if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__)
281
      // If on Unix, add the path of the current executable to the module search path
282
      // as windows would do
283

284
      union cast_union
285
      {
286
        Boxed_Value (ChaiScript_Basic::*in_ptr)(const std::string&);
287
        void *out_ptr;
288
      };
289

290
      Dl_info rInfo; 
291 1
      memset( &rInfo, 0, sizeof(rInfo) ); 
292
      cast_union u;
293 1
      u.in_ptr = &ChaiScript_Basic::use;
294
      if ( (dladdr(static_cast<void*>(u.out_ptr), &rInfo) != 0) && (rInfo.dli_fname != nullptr) ) { 
295 1
        std::string dllpath(rInfo.dli_fname);
296 1
        const size_t lastslash = dllpath.rfind('/');
297 1
        if (lastslash != std::string::npos)
298
        {
299 1
          dllpath.erase(lastslash);
300
        }
301

302
        // Let's see if this is a link that we should expand
303 1
        std::vector<char> buf(2048);
304 1
        const auto pathlen = readlink(dllpath.c_str(), &buf.front(), buf.size());
305
        if (pathlen > 0 && static_cast<size_t>(pathlen) < buf.size())
306
        {
307 0
          dllpath = std::string(&buf.front(), static_cast<size_t>(pathlen));
308
        }
309

310 1
        m_module_paths.insert(m_module_paths.begin(), dllpath+"/");
311
      }
312
#endif
313 1
      build_eval_system(t_lib, t_opts);
314
    }
315

316
#ifndef CHAISCRIPT_NO_DYNLOAD
317
    /// \brief Constructor for ChaiScript.
318
    /// 
319
    /// This version of the ChaiScript constructor attempts to find the stdlib module to load
320
    /// at runtime generates an error if it cannot be found.
321
    ///
322
    /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module
323
    /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file
324 1
    explicit ChaiScript_Basic(std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
325
                     std::vector<std::string> t_module_paths = {},
326
                     std::vector<std::string> t_use_paths = {},
327
                     const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options())
328 1
      : ChaiScript_Basic({}, std::move(parser), t_module_paths, t_use_paths, t_opts)
329
    {
330
      try {
331
        // attempt to load the stdlib
332 1
        load_module("chaiscript_stdlib-" + Build_Info::version());
333 0
      } catch (const exception::load_module_error &t_err) {
334
        std::cout << "An error occured while trying to load the chaiscript standard library.\n"
335
                  << "\n"
336
                  << "You must either provide a standard library, or compile it in.\n"
337
                  << "For an example of compiling the standard library in,\n"
338
                  << "see: https://gist.github.com/lefticus/9456197\n"
339
                  << "Compiling the stdlib in is the recommended and MOST SUPPORTED method.\n"
340
                  << "\n"
341
                  << "\n"
342 0
                  << t_err.what();
343 0
        throw;
344
      }
345
    }
346
#else // CHAISCRIPT_NO_DYNLOAD
347
    explicit ChaiScript_Basic(std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
348
                              std::vector<std::string> t_module_paths = {},
349
                              std::vector<std::string> t_use_paths = {},
350
                              const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options()) = delete;
351
#endif
352

353 1
    parser::ChaiScript_Parser_Base &get_parser() noexcept
354
    {
355 1
      return *m_parser;
356
    }
357

358 1
    const Boxed_Value eval(const AST_Node &t_ast)
359
    {
360
      try {
361 1
        return t_ast.eval(chaiscript::detail::Dispatch_State(m_engine));
362 1
      } catch (const exception::eval_error &t_ee) {
363 1
        throw Boxed_Value(t_ee);
364
      }
365
    }
366

367 1
    AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false)
368
    {
369 1
      auto ast = m_parser->parse(t_input, "PARSE");
370 1
      if (t_debug_print) {
371 0
        m_parser->debug_print(*ast);
372
      }
373 1
      return ast;
374
    }
375

376

377 1
    std::string get_type_name(const Type_Info &ti) const
378
    {
379 1
      return m_engine.get_type_name(ti);
380
    }
381

382
    template<typename T>
383 1
    std::string get_type_name() const
384
    {
385 1
      return get_type_name(user_type<T>());
386
    }
387

388

389
    /// \brief Loads and parses a file. If the file is already open, it is not
390
    /// reloaded. The use paths specified at ChaiScript construction time are 
391
    /// searched for the requested file.
392
    ///
393
    /// \param[in] t_filename Filename to load and evaluate
394 1
    Boxed_Value use(const std::string &t_filename)
395
    {
396 1
      for (const auto &path : m_use_paths)
397
      {
398 1
        const auto appendedpath = path + t_filename;
399
        try {
400

401 1
          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
402 1
          chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l2(m_mutex);
403

404 1
          Boxed_Value retval;
405

406 1
          if (m_used_files.count(appendedpath) == 0)
407
          {
408 1
            l2.unlock();
409 1
            retval = eval_file(appendedpath);
410 1
            l2.lock();
411 1
            m_used_files.insert(appendedpath);
412
          }
413

414 1
          return retval; // return, we loaded it, or it was already loaded
415 1
        } catch (const exception::file_not_found_error &e) {
416 1
          if (e.filename != appendedpath) {
417
            // a nested file include failed
418 1
            throw;
419
          }
420
          // failed to load, try the next path
421
        }
422
      }
423

424
      // failed to load by any name
425 1
      throw exception::file_not_found_error(t_filename);
426
    }
427

428
    /// \brief Adds a constant object that is available in all contexts and to all threads
429
    /// \param[in] t_bv Boxed_Value to add as a global
430
    /// \param[in] t_name Name of the value to add
431
    /// \throw chaiscript::exception::global_non_const If t_bv is not a constant object
432
    /// \sa Boxed_Value::is_const
433 0
    ChaiScript_Basic &add_global_const(const Boxed_Value &t_bv, const std::string &t_name)
434
    {
435 0
      Name_Validator::validate_object_name(t_name);
436 0
      m_engine.add_global_const(t_bv, t_name);
437 0
      return *this;
438
    }
439

440
    /// \brief Adds a mutable object that is available in all contexts and to all threads
441
    /// \param[in] t_bv Boxed_Value to add as a global
442
    /// \param[in] t_name Name of the value to add
443
    /// \warning The user is responsible for making sure the object is thread-safe if necessary
444
    ///          ChaiScript is thread-safe but provides no threading locking mechanism to the script
445 1
    ChaiScript_Basic &add_global(const Boxed_Value &t_bv, const std::string &t_name)
446
    {
447 1
      Name_Validator::validate_object_name(t_name);
448 1
      m_engine.add_global(t_bv, t_name);
449 1
      return *this;
450
    }
451

452 1
    ChaiScript_Basic &set_global(const Boxed_Value &t_bv, const std::string &t_name)
453
    {
454 1
      Name_Validator::validate_object_name(t_name);
455 1
      m_engine.set_global(t_bv, t_name);
456 1
      return *this;
457
    }
458

459
    /// \brief Represents the current state of the ChaiScript system. State and be saved and restored
460
    /// \warning State object does not contain the user defined type conversions of the engine. They
461
    ///          are left out due to performance considerations involved in tracking the state
462
    /// \sa ChaiScript::get_state
463
    /// \sa ChaiScript::set_state
464
    struct State
465
    {
466
      std::set<std::string> used_files;
467
      chaiscript::detail::Dispatch_Engine::State engine_state;
468
      std::set<std::string> active_loaded_modules;
469
    };
470

471
    /// \brief Returns a state object that represents the current state of the global system
472
    ///
473
    /// The global system includes the reserved words, global const objects, functions and types.
474
    /// local variables are thread specific and not included.
475
    ///
476
    /// \return Current state of the global system
477
    ///
478
    /// \b Example:
479
    ///
480
    /// \code
481
    /// chaiscript::ChaiScript chai;
482
    /// chaiscript::ChaiScript::State s = chai.get_state(); // represents bootstrapped initial state
483
    /// \endcode
484 1
    State get_state() const
485
    {
486 1
      chaiscript::detail::threading::lock_guard<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
487 1
      chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l2(m_mutex);
488

489 1
      State s;
490 1
      s.used_files = m_used_files;
491 1
      s.engine_state = m_engine.get_state();
492 1
      s.active_loaded_modules = m_active_loaded_modules;
493 1
      return s;
494
    }
495

496
    /// \brief Sets the state of the system
497
    ///
498
    /// The global system includes the reserved words, global objects, functions and types.
499
    /// local variables are thread specific and not included.
500
    ///
501
    /// \param[in] t_state New state to set
502
    ///
503
    /// \b Example:
504
    /// \code
505
    /// chaiscript::ChaiScript chai;
506
    /// chaiscript::ChaiScript::State s = chai.get_state(); // get initial state
507
    /// chai.add(chaiscript::fun(&somefunction), "somefunction");
508
    /// chai.set_state(s); // restore initial state, which does not have the recently added "somefunction"
509
    /// \endcode
510 1
    void set_state(const State &t_state)
511
    {
512 1
      chaiscript::detail::threading::lock_guard<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
513 1
      chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l2(m_mutex);
514

515 1
      m_used_files = t_state.used_files;
516 1
      m_active_loaded_modules = t_state.active_loaded_modules;
517 1
      m_engine.set_state(t_state.engine_state);
518
    }
519

520
    /// \returns All values in the local thread state, added through the add() function
521 1
    std::map<std::string, Boxed_Value> get_locals() const
522
    {
523 1
      return m_engine.get_locals();
524
    }
525

526
    /// \brief Sets all of the locals for the current thread state.
527
    ///
528
    /// \param[in] t_locals The map<name, value> set of variables to replace the current state with
529
    ///
530
    /// Any existing locals are removed and the given set of variables is added
531 1
    void set_locals(const std::map<std::string, Boxed_Value> &t_locals)
532
    {
533 1
      m_engine.set_locals(t_locals);
534
    }
535

536
    /// \brief Adds a type, function or object to ChaiScript. Objects are added to the local thread state.
537
    /// \param[in] t_t Item to add
538
    /// \param[in] t_name Name of item to add
539
    /// \returns Reference to current ChaiScript object
540
    /// 
541
    /// \b Examples:
542
    /// \code
543
    /// chaiscript::ChaiScript chai;
544
    /// chai.add(chaiscript::user_type<MyClass>(), "MyClass"); // Add explicit type info (not strictly necessary)
545
    /// chai.add(chaiscript::fun(&MyClass::function), "function"); // Add a class method
546
    /// MyClass obj;
547
    /// chai.add(chaiscript::var(&obj), "obj"); // Add a pointer to a locally defined object
548
    /// \endcode
549
    ///
550
    /// \sa \ref adding_items
551
    template<typename T>
552 1
    ChaiScript_Basic &add(const T &t_t, const std::string &t_name)
553
    {
554 1
      Name_Validator::validate_object_name(t_name);
555 1
      m_engine.add(t_t, t_name);
556 1
      return *this;
557
    }
558

559
    /// \brief Add a new conversion for upcasting to a base class
560
    /// \sa chaiscript::base_class
561
    /// \param[in] d Base class / parent class 
562
    ///
563
    /// \b Example:
564
    /// \code
565
    /// chaiscript::ChaiScript chai;
566
    /// chai.add(chaiscript::base_class<std::runtime_error, chaiscript::dispatch_error>());
567
    /// \endcode
568 1
    ChaiScript_Basic &add(const Type_Conversion &d)
569
    {
570 1
      m_engine.add(d);
571 1
      return *this;
572
    }
573

574
    /// \brief Adds all elements of a module to ChaiScript runtime
575
    /// \param[in] t_p The module to add.
576
    /// \sa chaiscript::Module
577 1
    ChaiScript_Basic &add(const ModulePtr &t_p)
578
    {
579 1
      t_p->apply(*this, this->get_eval_engine());
580 1
      return *this;
581
    }
582

583
    /// \brief Load a binary module from a dynamic library. Works on platforms that support
584
    ///        dynamic libraries.
585
    /// \param[in] t_module_name Name of the module to load
586
    ///
587
    /// The module is searched for in the registered module path folders (chaiscript::ChaiScript::ChaiScript)
588
    /// and with standard prefixes and postfixes: ("lib"|"")\<t_module_name\>(".dll"|".so"|".bundle"|"").
589
    ///
590
    /// Once the file is located, the system looks for the symbol "create_chaiscript_module_\<t_module_name\>".
591
    /// If no file can be found matching the search criteria and containing the appropriate entry point 
592
    /// (the symbol mentioned above), an exception is thrown.
593
    ///
594
    /// \throw chaiscript::exception::load_module_error In the event that no matching module can be found.
595 1
    std::string load_module(const std::string &t_module_name)
596
    {
597
#ifdef CHAISCRIPT_NO_DYNLOAD
598
      throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)");
599
#else
600 1
      std::vector<exception::load_module_error> errors;
601 1
      std::string version_stripped_name = t_module_name;
602 1
      size_t version_pos = version_stripped_name.find("-" + Build_Info::version());
603 1
      if (version_pos != std::string::npos)
604
      {
605 1
        version_stripped_name.erase(version_pos);
606
      }
607

608 1
      std::vector<std::string> prefixes{"lib", "cyg", ""};
609

610 1
      std::vector<std::string> postfixes{".dll", ".so", ".bundle", ""};
611

612 1
      for (auto & elem : m_module_paths)
613
      {
614 1
        for (auto & prefix : prefixes)
615
        {
616 1
          for (auto & postfix : postfixes)
617
          {
618
            try {
619 1
              const auto name = elem + prefix + t_module_name + postfix;
620
              // std::cerr << "trying location: " << name << '\n';
621 1
              load_module(version_stripped_name, name);
622 1
              return name;
623 1
            } catch (const chaiscript::exception::load_module_error &e) {
624
              // std::cerr << "error: " << e.what() << '\n';
625 1
              errors.push_back(e);
626
              // Try next set
627
            }
628
          }
629
        }
630
      }
631

632 1
      throw chaiscript::exception::load_module_error(t_module_name, errors);
633
#endif
634
    }
635

636
    /// \brief Load a binary module from a dynamic library. Works on platforms that support
637
    ///        dynamic libraries.
638
    ///
639
    /// \param[in] t_module_name Module name to load
640
    /// \param[in] t_filename Ignore normal filename search process and use specific filename
641
    ///
642
    /// \sa ChaiScript::load_module(const std::string &t_module_name)
643 1
    void load_module(const std::string &t_module_name, const std::string &t_filename)
644
    {
645 1
      chaiscript::detail::threading::lock_guard<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
646

647 1
      if (m_loaded_modules.count(t_module_name) == 0)
648
      {
649 1
        detail::Loadable_Module_Ptr lm(new detail::Loadable_Module(t_module_name, t_filename));
650 1
        m_loaded_modules[t_module_name] = lm;
651 1
        m_active_loaded_modules.insert(t_module_name);
652 1
        add(lm->m_moduleptr);
653 0
      } else if (m_active_loaded_modules.count(t_module_name) == 0) {
654 0
        m_active_loaded_modules.insert(t_module_name);
655 0
        add(m_loaded_modules[t_module_name]->m_moduleptr);
656
      } 
657
    }
658

659

660
    /// \brief Evaluates a string. Equivalent to ChaiScript::eval.
661
    ///
662
    /// \param[in] t_script Script to execute
663
    /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
664
    ///
665
    /// \return result of the script execution
666
    /// 
667
    /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
668 1
    Boxed_Value operator()(const std::string &t_script, const Exception_Handler &t_handler = Exception_Handler())
669
    {
670 1
      return eval(t_script, t_handler);
671
    }
672

673
    /// \brief Evaluates a string and returns a typesafe result.
674
    ///
675
    /// \tparam T Type to extract from the result value of the script execution
676
    /// \param[in] t_input Script to execute
677
    /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
678
    /// \param[in] t_filename Optional filename to report to the user for where the error occured. Useful
679
    ///                       in special cases where you are loading a file internally instead of using eval_file
680
    ///
681
    /// \return result of the script execution
682
    /// 
683
    /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
684
    /// \throw chaiscript::exception::bad_boxed_cast In the case that evaluation succeeds but the result value cannot be converted
685
    ///        to the requested type.
686
    template<typename T>
687 1
    T eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__")
688
    {
689 1
      return m_engine.boxed_cast<T>(eval(t_input, t_handler, t_filename));
690
    }
691

692
    /// \brief casts an object while applying any Dynamic_Conversion available
693
    template<typename Type>
694 1
      decltype(auto) boxed_cast(const Boxed_Value &bv) const
695
      {
696 1
        return(m_engine.boxed_cast<Type>(bv));
697
      }
698
 
699

700
    /// \brief Evaluates a string.
701
    ///
702
    /// \param[in] t_input Script to execute
703
    /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
704
    /// \param[in] t_filename Optional filename to report to the user for where the error occurred. Useful
705
    ///                       in special cases where you are loading a file internally instead of using eval_file
706
    ///
707
    /// \return result of the script execution
708
    /// 
709
    /// \throw exception::eval_error In the case that evaluation fails.
710 1
    Boxed_Value eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__")
711
    {
712
      try {
713 1
        return do_eval(t_input, t_filename);
714 1
      } catch (Boxed_Value &bv) {
715 1
        if (t_handler) {
716 1
          t_handler->handle(bv, m_engine);
717
        }
718 1
        throw;
719
      }
720
    }
721

722
    /// \brief Loads the file specified by filename, evaluates it, and returns the result.
723
    /// \param[in] t_filename File to load and parse.
724
    /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
725
    /// \return result of the script execution
726
    /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
727 1
    Boxed_Value eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) {
728 1
      return eval(load_file(t_filename), t_handler, t_filename);
729
    }
730

731
    /// \brief Loads the file specified by filename, evaluates it, and returns the type safe result.
732
    /// \tparam T Type to extract from the result value of the script execution
733
    /// \param[in] t_filename File to load and parse.
734
    /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
735
    /// \return result of the script execution
736
    /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
737
    /// \throw chaiscript::exception::bad_boxed_cast In the case that evaluation succeeds but the result value cannot be converted
738
    ///        to the requested type.
739
    template<typename T>
740
    T eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) {
741
      return m_engine.boxed_cast<T>(eval_file(t_filename, t_handler));
742
    }
743

744
    /// \brief Imports a namespace object into the global scope of this ChaiScript instance.
745
    /// \param[in] t_namespace_name Name of the namespace to import.
746
    /// \throw std::runtime_error In the case that the namespace name was never registered.
747 1
    void import(const std::string& t_namespace_name)
748
    {
749 1
       chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
750

751 1
       if (m_engine.get_scripting_objects().count(t_namespace_name)) {
752 0
          throw std::runtime_error("Namespace: " + t_namespace_name + " was already defined");
753
       }
754 1
       else if (m_namespace_generators.count(t_namespace_name)) {
755 1
          m_engine.add_global(var(std::ref(m_namespace_generators[t_namespace_name]())), t_namespace_name);
756
       }
757
       else {
758 0
          throw std::runtime_error("No registered namespace: " + t_namespace_name);
759
       }
760
    }
761

762
    /// \brief Registers a namespace generator, which delays generation of the namespace until it is imported, saving memory if it is never used.
763
    /// \param[in] t_namespace_generator Namespace generator function.
764
    /// \param[in] t_namespace_name Name of the Namespace function being registered.
765
    /// \throw std::runtime_error In the case that the namespace name was already registered.
766 1
    void register_namespace(const std::function<void(Namespace&)>& t_namespace_generator, const std::string& t_namespace_name)
767
    {
768 1
       chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
769

770 1
       if (!m_namespace_generators.count(t_namespace_name)) {
771
          // contain the namespace object memory within the m_namespace_generators map
772 1
          m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace& { t_namespace_generator(space); return space; }));
773
       }
774
       else {
775 0
          throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered.");
776
       }
777
    }
778
  };
779

780
}
781
#endif /* CHAISCRIPT_ENGINE_HPP_ */
782

Read our documentation on viewing source code .

Loading