Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding basic namespace handling #290

Merged
merged 11 commits into from Nov 21, 2017
31 changes: 31 additions & 0 deletions cheatsheet.md
Expand Up @@ -166,6 +166,23 @@ chai.add_global(chaiscript::var(somevar), "somevar"); // global non-const, throw
chai.set_global(chaiscript::var(somevar), "somevar"); // global non-const, overwrites existing object
```

## Adding Namespaces

Namespaces will not be populated until `import` is called.
This saves memory and computing costs if a namespace is not imported into every ChaiScript instance.
```
chai.register_namespace([](chaiscript::Namespace& math) {
math["pi"] = chaiscript::const_var(3.14159);
math["sin"] = chaiscript::var(chaiscript::fun([](const double x) { return sin(x); })); },
"math");
```

Import namespace in ChaiScript
```
import("math")
print(math.pi) // prints 3.14159
```

# Using STL
ChaiScript recognize many types from STL, but you have to add specific instantiation yourself.

Expand Down Expand Up @@ -479,6 +496,20 @@ o.f = fun(y) { print(this.x + y); }
o.f(10); // prints 13
```

## Namespaces

Namespaces in ChaiScript are Dynamic Objects with global scope

```
namespace("math") // create a new namespace

math.square = fun(x) { x * x } // add a function to the "math" namespace
math.sum_squares = fun(x, y) { math.square(x) + math.square(y) }

print(math.square(4)) // prints 16
print(math.sum_squares(2, 5)) // prints 29
```

### Option Explicit

If you want to disable dynamic parameter definitions, you can `set_explicit`.
Expand Down
44 changes: 43 additions & 1 deletion include/chaiscript/language/chaiscript_engine.hpp
Expand Up @@ -54,7 +54,9 @@
#include "../dispatchkit/exception_specification.hpp"

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

namespace detail
{
Expand All @@ -79,6 +81,8 @@ namespace chaiscript

chaiscript::detail::Dispatch_Engine m_engine;

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

/// Evaluates the given string in by parsing it and running the results through the evaluator
Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false)
{
Expand Down Expand Up @@ -195,6 +199,9 @@ namespace chaiscript
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");
m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global");
m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global");

m_engine.add(fun([this](const std::string& t_namespace_name) { register_namespace([](Namespace& space) {}, t_namespace_name); import(t_namespace_name); }), "namespace");
m_engine.add(fun([this](const std::string& t_namespace_name) { import(t_namespace_name); }), "import");
}


Expand Down Expand Up @@ -703,6 +710,41 @@ explicit ChaiScript_Basic(std::unique_ptr<parser::ChaiScript_Parser_Base> &&pars
T eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) {
return m_engine.boxed_cast<T>(eval_file(t_filename, t_handler));
}

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

if (m_engine.get_scripting_objects().count(t_namespace_name)) {
throw std::runtime_error("Namespace: " + t_namespace_name + " was already defined");
}
else if (m_namespace_generators.count(t_namespace_name)) {
m_engine.add_global(var(std::ref(m_namespace_generators[t_namespace_name]())), t_namespace_name);
}
else {
throw std::runtime_error("No registered namespace: " + t_namespace_name);
}
}

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

if (!m_namespace_generators.count(t_namespace_name)) {
// contain the namespace object memory within the m_namespace_generators map
m_namespace_generators.emplace(std::make_pair(t_namespace_name, [=, space = Namespace()]() mutable -> Namespace& { t_namespace_generator(space); return space; }));
}
else {
throw std::runtime_error("Namespace: " + t_namespace_name + " was already registered.");
}
}
};

}
Expand Down
7 changes: 7 additions & 0 deletions unittests/namespaces.chai
@@ -0,0 +1,7 @@
namespace("math")

math.square = fun(x) { x * x }
math.sum_squares = fun(x, y) { math.square(x) + math.square(y) }

assert_equal(16, math.square(4))
assert_equal(29, math.sum_squares(2, 5))
9 changes: 9 additions & 0 deletions unittests/namespaces_nested_copy.chai
@@ -0,0 +1,9 @@
namespace("parent")
namespace("child")

child.x = 3.0
parent.child = child
parent.child.x = 5.0

assert_equal(3.0, child.x)
assert_equal(5.0, parent.child.x)