diff --git a/src/ant/ant/antObject.cc b/src/ant/ant/antObject.cc index 1a486c915..e71f3bac7 100644 --- a/src/ant/ant/antObject.cc +++ b/src/ant/ant/antObject.cc @@ -437,7 +437,7 @@ public: throw tl::EvalError (tl::to_string (tr ("Annotation function must not have arguments")), context); } - const Object &obj = mp_eval->obj (); + const ant::Object &obj = mp_eval->obj (); const db::DFTrans &trans = mp_eval->trans (); if (m_function == 'L') { @@ -471,18 +471,18 @@ public: } } - db::DPoint p1 (const Object &obj) const + db::DPoint p1 (const ant::Object &obj) const { return obj.seg_p1 (m_index); } - db::DPoint p2 (const Object &obj) const + db::DPoint p2 (const ant::Object &obj) const { return obj.seg_p2 (m_index); } double - delta_x (const Object &obj, const db::DFTrans &t) const + delta_x (const ant::Object &obj, const db::DFTrans &t) const { double dx = ((t * p2 (obj)).x () - (t * p1 (obj)).x ()); @@ -495,7 +495,7 @@ public: } double - delta_y (const Object &obj, const db::DFTrans &t) const + delta_y (const ant::Object &obj, const db::DFTrans &t) const { double dy = ((t * p2 (obj)).y () - (t * p1 (obj)).y ()); diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index 0b0fb4619..f55702a51 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -590,6 +590,12 @@ static void def_func (tl::Eval *eval, const std::string &name, FunctionBody *fun eval->define_function (name, func); } +static void def_global_func (const std::string &name, FunctionBody *func) +{ + func->keep (); + tl::Eval::define_global_function (name, func); +} + Class decl_FunctionBody ("tl", "FunctionBody", gsi::method ("with_kwargs=", &FunctionBody::set_with_kwargs, gsi::arg ("f"), "@brief Sets a value indicating whether this function accepts keyword arguments.\n" @@ -625,12 +631,115 @@ Class decl_FunctionBody ("tl", "FunctionBody", "This class has been introduced in version 0.29.6." ); +tl::Eval *new_expr_ctx0 () +{ + return new tl::Eval (); +} + +tl::Eval *new_expr_ctx1 (tl::Eval *parent) +{ + return new tl::Eval (parent); +} + +tl::Eval *new_expr_ctx2 (tl::Eval *global, tl::Eval *parent) +{ + return new tl::Eval (global, parent); +} + +void import3 (tl::Eval *eval, tl::Eval *from, const std::string &name) +{ + tl::Variant *var = from->var (name); + if (var) { + eval->set_var (name, *var); + } else { + tl::EvalFunction *func = from->function (name); + if (func) { + eval->define_function (name, func); + } + } +} + +void import4 (tl::Eval *eval, tl::Eval *from, const std::vector &names) +{ + for (auto i = names.begin (); i != names.end (); ++i) { + import3 (eval, from, *i); + } +} + +void import1 (tl::Eval *eval, const std::string &name) +{ + import3 (eval, &tl::Eval::global_context (), name); +} + +void import2 (tl::Eval *eval, const std::vector &names) +{ + import4 (eval, &tl::Eval::global_context (), names); +} + Class decl_ExpressionContext ("tl", "ExpressionContext", + gsi::constructor ("new", &new_expr_ctx0, + "@brief Creates a new expression context\n" + "The expression context acts as a namespace for variables and functions. " + "This version of the context is connected to the global, singleton context. " + "There are other constructors available for creating expression contexts with " + "a parent context or connected to another global context.\n" + ) + + gsi::constructor ("new", &new_expr_ctx1, gsi::arg ("parent"), + "@brief Creates a context with a parent context.\n" + "This context uses a parent context which is searched for variables and functions " + "when not local definition can be found. This version of the context also connects to " + "the global singleton context.\n" + "\n" + "This constructor was introduced in version 0.29.6." + ) + + gsi::constructor ("new", &new_expr_ctx2, gsi::arg ("global"), gsi::arg ("parent"), + "@brief Creates a context with a parent context and connecting to a separate global context.\n" + "This version allows specifying a global and parent context. The global context is not " + "the singleton context in this case. Specifically, this methods allows creating a detached " + "context by using 'nil' for both global and parent contexts. Such a context provides no " + "specific definitions and can be used to establish a safe environment without access to " + "higher-level classes.\n" + "\n" + "@code\n" + "ctx = RBA::ExpressionContext::new(nil, nil)\n" + "# imports the 'Box' class from the global singleton namespace\n" + "ctx.import('Box')\n" + "# Box is the only class that can be used here:\n" + "ctx.eval('Box(0,0,100,200)')\n" + "@/code\n" + "\n" + "This constructor was introduced in version 0.29.6." + ) + + gsi::method_ext ("import", &import1, gsi::arg ("name"), + "@brief Imports a variable from the global, singleton namespace.\n" + "This method can be used for importing classes from the global namespace and make it accessible to " + "this context, even if it is not connected to the global singleton one.\n" + "\n" + "This method has been introduced in version 0.29.6." + ) + + gsi::method_ext ("import", &import2, gsi::arg ("names"), + "@brief Imports variables from the global, singleton namespace.\n" + "This variant allows specifying a list of names to import.\n" + "\n" + "This method has been introduced in version 0.29.6." + ) + + gsi::method_ext ("import", &import3, gsi::arg ("from"), gsi::arg ("name"), + "@brief Import a variable from the given context.\n" + "This variant allows to give a source context.\n" + "\n" + "This method has been introduced in version 0.29.6." + ) + + gsi::method_ext ("import", &import4, gsi::arg ("from"), gsi::arg ("names"), + "@brief Imports variables from the given context.\n" + "This variant allows to give a source context and a list of names to import.\n" + "\n" + "This method has been introduced in version 0.29.6." + ) + gsi::method ("var", &tl::Eval::set_var, gsi::arg ("name"), gsi::arg ("value"), - "@brief Defines a variable with the given name and value\n" + "@brief Defines a variable with the given name and value.\n" ) + gsi::method_ext ("func", &def_func, gsi::arg ("name"), gsi::arg ("body"), - "@brief Defines a function with the given name and function body\n" + "@brief Defines a function with the given name and function body.\n" "The function body is an implementation of the \\FunctionBody class. To use it, create a subclass, i.e.\n" "\n" "@code\n" @@ -651,9 +760,18 @@ Class decl_ExpressionContext ("tl", "ExpressionContext", "\n" "This method has been introduced in version 0.29.6." ) + + gsi::method ("global_func", &def_global_func, gsi::arg ("name"), gsi::arg ("body"), + "Defines a function in the global namespace.\n" + "This method defines a function in the global singleton namespace. It works like \\func and acts " + "on the global namespace like \\global_var.\n" + "Note that the new function only becomes visible to contexts that connect to the global context.\n" + "\n" + "It has been introduced in version 0.29.6." + ) + gsi::method ("global_var", &tl::Eval::set_global_var, gsi::arg ("name"), gsi::arg ("value"), "@brief Defines a global variable with the given name and value\n" - "Global variables are available to all expressions sharing the same global context." + "This method defines a variable in the global singleton namespace. " + "Note that the new variable only becomes visible to contexts that connect to the global context.\n" ) + gsi::method ("eval", &tl::Eval::eval, gsi::arg ("expr"), "@brief Compiles and evaluates the given expression in this context\n" diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index 6853a5729..ef382f5f9 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -3184,14 +3184,11 @@ Eval::Eval (Eval *global, Eval *parent, bool sloppy) Eval::~Eval () { - for (std::map ::iterator f = m_local_functions.begin (); f != m_local_functions.end (); ++f) { - delete f->second; - } m_local_functions.clear (); } void -Eval::check () +Eval::check () const { if (m_has_parent) { if (! mp_parent.get ()) { @@ -3227,11 +3224,7 @@ Eval::var (const std::string &name) void Eval::define_function (const std::string &name, EvalFunction *function) { - EvalFunction *&f = m_local_functions.insert (std::make_pair (name, (EvalFunction *) 0)).first->second; - if (f != 0) { - delete f; - } - f = function; + m_local_functions [name].reset (function); } EvalFunction * @@ -3239,7 +3232,7 @@ Eval::function (const std::string &name) { auto f = m_local_functions.find (name); if (f != m_local_functions.end ()) { - return f->second; + return f->second.get (); } else { return 0; } @@ -4040,13 +4033,11 @@ Eval::resolve_name (const std::string &t, const EvalFunction *&function, const t value = 0; var = 0; - std::map ::const_iterator f; - f = m_local_functions.find (t); + auto f = m_local_functions.find (t); if (f != m_local_functions.end ()) { - function = f->second; + function = f->second.get (); } else if ((function = EvalStaticFunction::function_by_name (t)) == 0) { - std::map::iterator v; - v = m_local_vars.find (t); + auto v = m_local_vars.find (t); if (v != m_local_vars.end ()) { var = &v->second; } else { @@ -4054,12 +4045,11 @@ Eval::resolve_name (const std::string &t, const EvalFunction *&function, const t } } - if (! function && ! value && ! var) { - if (mp_parent) { - mp_parent->resolve_name (t, function, value, var); - } else if (mp_global) { - mp_global->resolve_name (t, function, value, var); - } + if (mp_parent && ! function && ! value && ! var) { + mp_parent->resolve_name (t, function, value, var); + } + if (mp_global && ! function && ! value && ! var) { + mp_global->resolve_name (t, function, value, var); } } diff --git a/src/tl/tl/tlExpression.h b/src/tl/tl/tlExpression.h index ffef940e9..0bafb7da0 100644 --- a/src/tl/tl/tlExpression.h +++ b/src/tl/tl/tlExpression.h @@ -270,6 +270,7 @@ public: * @brief A base class for a function */ class TL_PUBLIC EvalFunction + : public tl::Object { public: /** @@ -583,7 +584,7 @@ public: /** * @brief Checks the contexts and throws an exception if one of them got lost */ - void check (); + void check () const; private: friend class Expression; @@ -593,7 +594,7 @@ private: tl::weak_ptr mp_global; bool m_has_global; std::map m_local_vars; - std::map m_local_functions; + std::map > m_local_functions; bool m_sloppy; const ContextHandler *mp_ctx_handler; std::vector m_match_substrings; diff --git a/testdata/ruby/tlTest.rb b/testdata/ruby/tlTest.rb index 99f315f2f..bbd167899 100644 --- a/testdata/ruby/tlTest.rb +++ b/testdata/ruby/tlTest.rb @@ -274,7 +274,53 @@ class Tl_TestClass < TestBase pc._destroy - self.assert_equal(e1.eval, 5) + begin + e1.eval + self.assert_equal(true, false) + rescue => ex + self.assert_equal(ex.to_s, "Parent context was destroyed in Expression::eval") + end + + end + + # Parent contexts + def test_5_GlobalContext + + # this is a new disconnected context + pc = RBA::ExpressionContext::new(nil, nil) + pc.var("A", 10) + + # this is a new disconnected context + gc = RBA::ExpressionContext::new(nil, nil) + gc.var("B", 1.5) + + e = RBA::Expression::new(gc, pc) + e.text = "B * A" + self.assert_equal(e.eval, 15) + + # built-in functions still work + e.text = "pow(A,2)" + self.assert_equal(e.eval, 100) + + # but other classes don't + begin + e.text = "Box.new(1, 2, 3, 4)" + self.assert_equal(true, false) + rescue => ex + end + + # borrow "Box" from the global context (reference context) + e.import("Box") + + e.text = "Box.new(1, 2, 3, 4)" + self.assert_equal(e.eval.to_s, "(1,2;3,4)") + + # DBox still does not work + begin + e.text = "DBox.new(1, 2, 3, 4)" + self.assert_equal(true, false) + rescue => ex + end end