diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index bafa9cb7..4056a2eb 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -377,6 +377,7 @@ namespace chaiscript struct State { std::map > m_functions; + std::map m_function_objects; std::map m_global_objects; Type_Name_Map m_types; std::set m_reserved_words; @@ -414,7 +415,6 @@ namespace chaiscript * Set the value of an object, by name. If the object * is not available in the current scope it is created */ - /* void add(const Boxed_Value &obj, const std::string &name) { validate_object_name(name); @@ -432,7 +432,7 @@ namespace chaiscript add_object(name, obj); } - */ + /** * Adds a named object to the current scope @@ -496,28 +496,24 @@ namespace chaiscript } } - /** - * Swaps out the stack with a new stack - * \returns the old stack - * \param[in] s The new stack - */ - Stack set_stack(const Stack &s) - { - Stack old = m_stack_holder->stack; - m_stack_holder->stack = s; - return old; - } - Stack new_stack() const + /// Pushes a new stack on to the list of stacks + void new_stack() { Stack s(new Stack::element_type()); s->push_back(Scope()); - return s; + m_stack_holder->stacks.push_back(s); } + void pop_stack() + { + m_stack_holder->stacks.pop_back(); + } + + /// \returns the current stack Stack get_stack() const { - return m_stack_holder->stack; + return m_stack_holder->stacks.back(); } /** @@ -557,21 +553,7 @@ namespace chaiscript } // If all that failed, then check to see if it's a function - std::vector funcs = get_function(name); - - if (funcs.empty()) - { - throw std::range_error("Object not known: " + name); - } else { - if (funcs.size() == 1) - { - // Return the first item if there is only one, - // no reason to take the cast of the extra level of dispatch - return const_var(*funcs.begin()); - } else { - return Boxed_Value(Const_Proxy_Function(new Dispatch_Function(funcs))); - } - } + return get_function_object(name); } /** @@ -654,9 +636,26 @@ namespace chaiscript } else { return std::vector(); } - } + /// \returns a function object (Boxed_Value wrapper) if it exists + /// \throws std::range_error if it does not + Boxed_Value get_function_object(const std::string &t_name) const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const std::map &funs = get_function_objects_int(); + + std::map::const_iterator itr = funs.find(t_name); + + if (itr != funs.end()) + { + return const_var(itr->second); + } else { + throw std::range_error("Object not found: " + t_name); + } + } + /** * Return true if a function exists */ @@ -668,6 +667,57 @@ namespace chaiscript return functions.find(name) != functions.end(); } + + /// + /// Get a map of all objects that can be seen from the current scope in a scripting context + /// + std::map get_scripting_objects() const + { + // We don't want the current context, but one up if it exists + StackData &stack = (m_stack_holder->stacks.size()==1)?(*(m_stack_holder->stacks.back())):(*m_stack_holder->stacks[m_stack_holder->stacks.size()-2]); + + std::map retval; + + // note: map insert doesn't overwrite existing values, which is why this works + + for (StackData::reverse_iterator itr = stack.rbegin(); itr != stack.rend(); ++itr) + { + retval.insert(itr->begin(), itr->end()); + } + + // add the global values + { + chaiscript::detail::threading::shared_lock l(m_global_object_mutex); + + retval.insert(m_state.m_global_objects.begin(), m_state.m_global_objects.end()); + } + + return retval; + } + + + /// + /// Get a map of all functions that can be seen from a scripting context + /// + std::map get_function_objects() const + { + chaiscript::detail::threading::shared_lock l(m_mutex); + + const std::map &funs = get_function_objects_int(); + + std::map objs; + + for (std::map::const_iterator itr = funs.begin(); + itr != funs.end(); + ++itr) + { + objs.insert(std::make_pair(itr->first, const_var(itr->second))); + } + + return objs; + } + + /** * Get a vector of all registered functions */ @@ -855,7 +905,17 @@ namespace chaiscript */ StackData &get_stack_data() const { - return *(m_stack_holder->stack); + return *(m_stack_holder->stacks.back()); + } + + const std::map &get_function_objects_int() const + { + return m_state.m_function_objects; + } + + std::map &get_function_objects_int() + { + return m_state.m_function_objects; } const std::map > &get_functions_int() const @@ -989,6 +1049,8 @@ namespace chaiscript std::map >::iterator itr = funcs.find(t_name); + std::map &func_objs = get_function_objects_int(); + if (itr != funcs.end()) { std::vector &vec = itr->second; @@ -1004,11 +1066,15 @@ namespace chaiscript vec.push_back(t_f); std::stable_sort(vec.begin(), vec.end(), &function_less_than); + func_objs[t_name] = Proxy_Function(new Dispatch_Function(vec)); } else { std::vector vec; vec.push_back(t_f); funcs.insert(std::make_pair(t_name, vec)); + func_objs[t_name] = t_f; } + + } mutable chaiscript::detail::threading::shared_mutex m_mutex; @@ -1017,12 +1083,13 @@ namespace chaiscript struct Stack_Holder { Stack_Holder() - : stack(new StackData()) { - stack->push_back(Scope()); + Stack s(new StackData()); + s->push_back(Scope()); + stacks.push_back(s); } - Stack stack; + std::deque stacks; }; std::vector m_conversions; diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 4597a7d8..00261d14 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -213,6 +213,29 @@ namespace chaiscript chaiscript::detail::Dispatch_Engine &m_de; }; + + /// Creates a new scope then pops it on destruction + struct Stack_Push_Pop + { + Stack_Push_Pop(chaiscript::detail::Dispatch_Engine &t_de) + : m_de(t_de) + { + m_de.new_stack(); + } + + ~Stack_Push_Pop() + { + m_de.pop_stack(); + } + + + private: + // explicitly unimplemented copy and assignment + Stack_Push_Pop(const Scope_Push_Pop &); + Stack_Push_Pop& operator=(const Scope_Push_Pop &); + + chaiscript::detail::Dispatch_Engine &m_de; + }; } } } diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index a7ccc1d1..1da74f4d 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -322,6 +322,8 @@ namespace chaiscript m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::is_type, boost::ref(m_engine)), "is_type"); m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::type_name, boost::ref(m_engine)), "type_name"); m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::function_exists, boost::ref(m_engine)), "function_exists"); + m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::get_function_objects, boost::ref(m_engine)), "get_functions"); + m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::get_scripting_objects, boost::ref(m_engine)), "get_objects"); m_engine.add(fun(&chaiscript::detail::Dispatch_Engine::get_type_name, boost::ref(m_engine)), "name"); diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index f7aa2dc0..9b5fc784 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -190,36 +190,19 @@ namespace chaiscript } } - chaiscript::detail::Dispatch_Engine::Stack prev_stack = t_ss.get_stack(); - chaiscript::detail::Dispatch_Engine::Stack new_stack = t_ss.new_stack(); + Boxed_Value fn = this->children[0]->eval(t_ss); try { - Boxed_Value fn = this->children[0]->eval(t_ss); - - try { - t_ss.set_stack(new_stack); - const Boxed_Value &retval = (*boxed_cast(fn))(plb); - t_ss.set_stack(prev_stack); - return retval; - } - catch(const exception::dispatch_error &e){ - t_ss.set_stack(prev_stack); - throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); - } - catch(detail::Return_Value &rv) { - t_ss.set_stack(prev_stack); - return rv.retval; - } - catch(...) { - t_ss.set_stack(prev_stack); - throw; - } + chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); + const Boxed_Value &retval = (*boxed_cast(fn))(plb); + return retval; } - catch(exception::eval_error &) { - t_ss.set_stack(prev_stack); - throw; + catch(const exception::dispatch_error &e){ + throw exception::eval_error(std::string(e.what()) + " with function '" + this->children[0]->text + "'"); + } + catch(detail::Return_Value &rv) { + return rv.retval; } - } }; @@ -466,26 +449,17 @@ namespace chaiscript fun_name = this->children[i]->text; } - chaiscript::detail::Dispatch_Engine::Stack prev_stack = t_ss.get_stack(); - chaiscript::detail::Dispatch_Engine::Stack new_stack = t_ss.new_stack(); - try { - t_ss.set_stack(new_stack); + chaiscript::eval::detail::Stack_Push_Pop spp(t_ss); retval = t_ss.call_function(fun_name, plb); - t_ss.set_stack(prev_stack); } catch(const exception::dispatch_error &e){ - t_ss.set_stack(prev_stack); throw exception::eval_error(std::string(e.what()) + " for function: " + fun_name); } catch(detail::Return_Value &rv) { - t_ss.set_stack(prev_stack); retval = rv.retval; } - catch(...) { - t_ss.set_stack(prev_stack); - throw; - } + if (this->children[i]->identifier == AST_Node_Type::Array_Call) { for (size_t j = 1; j < this->children[i]->children.size(); ++j) { try { diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 2233efd9..d3b40c44 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -1465,12 +1465,9 @@ namespace chaiscript * Reads a switch statement from input */ bool Switch() { - bool retval = false; - size_t prev_stack_top = m_match_stack.size(); if (Keyword("switch")) { - retval = true; if (!Char('(')) { throw exception::eval_error("Incomplete 'switch' expression", File_Position(m_line, m_col), *m_filename); @@ -1483,8 +1480,6 @@ namespace chaiscript while (Eol()) {} if (Char('{')) { - retval = true; - while (Eol()) {} while (Case()) { @@ -1502,9 +1497,12 @@ namespace chaiscript } build_match(AST_NodePtr(new eval::Switch_AST_Node()), prev_stack_top); + return true; + + } else { + return false; } - return retval; } /** @@ -1994,7 +1992,6 @@ namespace chaiscript bool saw_eol = true; while (has_more) { - has_more = false; int prev_line = m_line; int prev_col = m_col; if (Def()) { diff --git a/unittests/pass_by_reference.chai b/unittests/pass_by_reference.chai new file mode 100644 index 00000000..89c7760b --- /dev/null +++ b/unittests/pass_by_reference.chai @@ -0,0 +1,19 @@ +def f(x) { x+= 2; } + +var i = 1; + +assert_equal(i, 1); + +f(i); + +assert_equal(i, 3); + +def g(x) { x+= " World"; } + +var s = "Hello"; + +assert_equal(s, "Hello"); + +g(s); + +assert_equal(s, "Hello World"); diff --git a/unittests/system_introspection.chai b/unittests/system_introspection.chai new file mode 100644 index 00000000..fc7350e6 --- /dev/null +++ b/unittests/system_introspection.chai @@ -0,0 +1,18 @@ + +var funcs = get_functions(); + +assert_true(funcs.size() > 0); +assert_true(funcs["to_string"].get_type_info().bare_equal(Function_type)); + + +var i = 1; +var objs = get_objects(); + +assert_true(objs.size() > 0); +assert_true(objs["i"].get_type_info().bare_equal(int_type)); +assert_true(objs.count("j") == 0); + + + + + diff --git a/unittests/temporary_lifetime.chai b/unittests/temporary_lifetime.chai new file mode 100644 index 00000000..ab6933ec --- /dev/null +++ b/unittests/temporary_lifetime.chai @@ -0,0 +1,3 @@ +for_each(range([1..10]), fun(x) {print(x);} ); + +assert_true(true); diff --git a/unittests/unit_test.inc b/unittests/unit_test.inc index d746e7bf..9d2fb91f 100644 --- a/unittests/unit_test.inc +++ b/unittests/unit_test.inc @@ -23,7 +23,7 @@ def assert_true(f) { if (!f) { - print("assert_false failure"); + print("assert_true failure"); exit(-1); } }