diff --git a/src/db/db/dbLayoutQuery.cc b/src/db/db/dbLayoutQuery.cc index 0bc0c3997..0faefb0cc 100644 --- a/src/db/db/dbLayoutQuery.cc +++ b/src/db/db/dbLayoutQuery.cc @@ -2427,9 +2427,9 @@ LayoutQuery::dump () const } void -LayoutQuery::execute (db::Layout &layout) +LayoutQuery::execute (db::Layout &layout, tl::Eval *context) { - LayoutQueryIterator iq (*this, &layout); + LayoutQueryIterator iq (*this, &layout, context); while (! iq.at_end ()) { ++iq; } diff --git a/src/db/db/dbLayoutQuery.h b/src/db/db/dbLayoutQuery.h index 48dd5e516..86b5bc3a1 100644 --- a/src/db/db/dbLayoutQuery.h +++ b/src/db/db/dbLayoutQuery.h @@ -468,8 +468,10 @@ public: * as "delete" or "with ... do". * It is basically equivalent to iterating over the query until it is * done. + * + * The context provides a way to define variables and functions. */ - void execute (db::Layout &layout); + void execute (db::Layout &layout, tl::Eval *context = 0); /** * @brief A dump method (for debugging) diff --git a/src/db/db/gsiDeclDbLayoutQuery.cc b/src/db/db/gsiDeclDbLayoutQuery.cc index 364f349d8..d45c350fe 100644 --- a/src/db/db/gsiDeclDbLayoutQuery.cc +++ b/src/db/db/gsiDeclDbLayoutQuery.cc @@ -75,8 +75,8 @@ struct LayoutQueryIteratorWrapper typedef void difference_type; typedef void pointer; - LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout) - : mp_iter (new db::LayoutQueryIterator (q, layout)) + LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout, tl::Eval *eval) + : mp_iter (new db::LayoutQueryIterator (q, layout, eval)) { // .. nothing yet .. } @@ -100,9 +100,9 @@ private: tl::shared_ptr mp_iter; }; -static LayoutQueryIteratorWrapper iterate (const db::LayoutQuery *q, const db::Layout *layout) +static LayoutQueryIteratorWrapper iterate (const db::LayoutQuery *q, const db::Layout *layout, tl::Eval *eval) { - return LayoutQueryIteratorWrapper (*q, layout); + return LayoutQueryIteratorWrapper (*q, layout, eval); } static tl::Variant iter_get (db::LayoutQueryIterator *iter, const std::string &name) @@ -187,18 +187,24 @@ Class decl_LayoutQuery ("db", "LayoutQuery", "This method allows detection of the properties available. Within the query, all of these " "properties can be obtained from the query iterator using \\LayoutQueryIterator#get.\n" ) + - gsi::method ("execute", &db::LayoutQuery::execute, gsi::arg("layout"), + gsi::method ("execute", &db::LayoutQuery::execute, gsi::arg("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"), "@brief Executes the query\n" "\n" "This method can be used to execute \"active\" queries such\n" "as \"delete\" or \"with ... do\".\n" "It is basically equivalent to iterating over the query until it is\n" "done.\n" + "\n" + "The context argument allows supplying an expression execution context. This context can be used for " + "example to supply variables for the execution. It has been added in version 0.26.\n" ) + - gsi::iterator_ext ("each", &iterate, gsi::arg ("layout"), + gsi::iterator_ext ("each", &iterate, gsi::arg ("layout"), gsi::arg ("context", (tl::Eval *) 0, "nil"), "@brief Executes the query and delivered the results iteratively.\n" "The argument to the block is a \\LayoutQueryIterator object which can be " "asked for specific results.\n" + "\n" + "The context argument allows supplying an expression execution context. This context can be used for " + "example to supply variables for the execution. It has been added in version 0.26.\n" ), "@brief A layout query\n" "Layout queries are the backbone of the \"Search & replace\" feature. Layout queries allow retrieval of " diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index 3997bbd9f..c282c33c8 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -444,12 +444,13 @@ namespace * @brief A convenience wrapper for the expression parser */ class ExpressionWrapper - : public tl::Object + : public tl::Eval, public gsi::ObjectBase { public: ExpressionWrapper () - : mp_eval (new tl::Eval ()) + : tl::Eval () { + // .. nothing yet .. } void parse (const std::string &e) @@ -457,20 +458,10 @@ public: mp_expr.reset (0); std::auto_ptr ex (new tl::Expression ()); - mp_eval->parse (*ex, e); + tl::Eval::parse (*ex, e); mp_expr.reset (ex.release ()); } - void set_var (const std::string &name, const tl::Variant &value) - { - mp_eval->set_var (name, value); - } - - static void set_global_var (const std::string &name, const tl::Variant &value) - { - tl::Eval::set_global_var (name, value); - } - tl::Variant eval () { if (mp_expr.get ()) { @@ -482,24 +473,23 @@ public: private: std::auto_ptr mp_expr; - std::auto_ptr mp_eval; }; -tl::Variant eval_expr (const std::string &e) +static tl::Variant eval_expr (const std::string &e) { ExpressionWrapper expr; expr.parse (e); return expr.eval (); } -ExpressionWrapper *new_expr1 (const std::string &e) +static ExpressionWrapper *new_expr1 (const std::string &e) { std::auto_ptr expr (new ExpressionWrapper ()); expr->parse (e); return expr.release (); } -ExpressionWrapper *new_expr2 (const std::string &e, const std::map &variables) +static ExpressionWrapper *new_expr2 (const std::string &e, const std::map &variables) { std::auto_ptr expr (new ExpressionWrapper ()); for (std::map::const_iterator v = variables.begin (); v != variables.end (); ++v) { @@ -523,7 +513,25 @@ namespace tl { namespace gsi { -Class decl_ExpressionWrapper ("tl", "Expression", +Class decl_ExpressionContext ("tl", "ExpressionContext", + gsi::method ("var", &tl::Eval::set_var, gsi::arg ("name"), gsi::arg ("value"), + "@brief Defines a variable with the given name and value\n" + ) + + 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" + ) + + gsi::method ("eval", &tl::Eval::eval, gsi::arg ("expr"), + "@brief Compiles and evaluates the given expression in this context\n" + "This method has been introduced in version 0.26." + ), + "@brief Represents the context of an expression evaluation\n" + "\n" + "The context provides a variable namespace for the expression evaluation.\n" + "\n" + "This class has been introduced in version 0.26 when \\Expression was separated into the execution and context part.\n" +); + +Class decl_ExpressionWrapper (decl_ExpressionContext, "tl", "Expression", gsi::constructor ("new", &new_expr1, gsi::arg ("expr"), "@brief Creates an expression evaluator\n" ) + @@ -534,12 +542,6 @@ Class decl_ExpressionWrapper ("tl", "Expression", gsi::method ("text=", &ExpressionWrapper::parse, gsi::arg ("expr"), "@brief Sets the given text as the expression." ) + - gsi::method ("var", &ExpressionWrapper::set_var, gsi::arg ("name"), gsi::arg ("value"), - "@brief Defines a variable with the given name and value\n" - ) + - gsi::method ("global_var", &ExpressionWrapper::set_global_var, gsi::arg ("name"), gsi::arg ("value"), - "@brief Defines a global variable with the given name and value\n" - ) + gsi::method ("eval", &ExpressionWrapper::eval, "@brief Evaluates the current expression and returns the result\n" ) + @@ -555,10 +557,9 @@ Class decl_ExpressionWrapper ("tl", "Expression", "\n" "An expression is 'compiled' into an Expression object and can be evaluated multiple times.\n" "\n" - "This class has been introduced in version 0.25.\n" + "This class has been introduced in version 0.25. In version 0.26 it was separated into execution and context.\n" ); - static tl::GlobPattern *new_glob_pattern (const std::string &s) { return new tl::GlobPattern (s); diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index 9cdd45452..1696ef35e 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -3127,7 +3127,7 @@ Expression::execute (EvalTarget &v) const Eval Eval::m_global; -Eval::Eval (const Eval *parent, bool sloppy) +Eval::Eval (Eval *parent, bool sloppy) : mp_parent (parent), m_sloppy (sloppy), mp_ctx_handler (0) { // .. nothing yet .. @@ -3867,14 +3867,14 @@ Eval::eval_atomic (ExpressionParserContext &ex, std::auto_ptr &n const tl::Variant *value = 0; tl::Variant *var = 0; - resolve_var_name (t, var); - if (! var) { - if (am == 2) { + if (am == 2) { + resolve_var_name (t, var); + if (! var) { set_var (t, tl::Variant ()); resolve_var_name (t, var); - } else { - resolve_name (t, function, value); } + } else { + resolve_name (t, function, value, var); } if (function) { @@ -3935,31 +3935,31 @@ Eval::resolve_var_name (const std::string &t, tl::Variant *&value) } void -Eval::resolve_name (const std::string &t, const EvalFunction *&function, const tl::Variant *&value) const +Eval::resolve_name (const std::string &t, const EvalFunction *&function, const tl::Variant *&value, tl::Variant *&var) { function = 0; value = 0; + var = 0; std::map ::const_iterator f; f = m_local_functions.find (t); if (f != m_local_functions.end ()) { function = f->second; } else if ((function = EvalStaticFunction::function_by_name (t)) == 0) { - - std::map::const_iterator v; + std::map::iterator v; v = m_local_vars.find (t); if (v != m_local_vars.end ()) { - value = &v->second; + var = &v->second; } else { value = EvalStaticConstant::constant_by_name (t); } } - if (! function && ! value) { + if (! function && ! value && ! var) { if (mp_parent) { - mp_parent->resolve_name (t, function, value); + mp_parent->resolve_name (t, function, value, var); } else if (this != &m_global) { - m_global.resolve_name (t, function, value); + m_global.resolve_name (t, function, value, var); } } } diff --git a/src/tl/tl/tlExpression.h b/src/tl/tl/tlExpression.h index 013f9a912..fc6bb8ede 100644 --- a/src/tl/tl/tlExpression.h +++ b/src/tl/tl/tlExpression.h @@ -367,7 +367,7 @@ public: * @param parent The parent evaluation context * @param sloppy True to enable sloppy evaluation for pure parsing */ - Eval (const Eval *parent = 0, bool sloppy = false); + Eval (Eval *parent = 0, bool sloppy = false); /** * @brief virtual dtor to enable dynamic_cast on derived classes. @@ -481,6 +481,11 @@ public: */ static std::string parse_expr (tl::Extractor &ex, bool top = true); + /** + * @brief A convenience method to evaluate an expression (by string) in this context + */ + tl::Variant eval (const std::string &expr); + /** * @brief Interpolate the string and return the result * @@ -508,14 +513,13 @@ public: private: friend class Expression; - const Eval *mp_parent; + Eval *mp_parent; std::map m_local_vars; std::map m_local_functions; bool m_sloppy; const ContextHandler *mp_ctx_handler; std::vector m_match_substrings; - tl::Variant eval (const std::string &expr); void eval_top (ExpressionParserContext &context, std::auto_ptr &v); void eval_assign (ExpressionParserContext &context, std::auto_ptr &v); void eval_if (ExpressionParserContext &context, std::auto_ptr &v); @@ -528,7 +532,7 @@ private: void eval_unary (ExpressionParserContext &context, std::auto_ptr &v); void eval_atomic (ExpressionParserContext &context, std::auto_ptr &v, int am); void eval_suffix (ExpressionParserContext &context, std::auto_ptr &v); - void resolve_name (const std::string &name, const EvalFunction *&function, const tl::Variant *&value) const; + void resolve_name (const std::string &name, const EvalFunction *&function, const tl::Variant *&value, tl::Variant *&var); void resolve_var_name (const std::string &name, tl::Variant *&value); static Eval m_global; diff --git a/testdata/python/tlTest.py b/testdata/python/tlTest.py index fb6b15474..52afee8a0 100644 --- a/testdata/python/tlTest.py +++ b/testdata/python/tlTest.py @@ -24,6 +24,11 @@ class TLTest(unittest.TestCase): def test_1(self): + ctx = pya.ExpressionContext() + self.assertEqual(ctx.eval("1+2"), 3) + ctx.var("a", 21) + self.assertEqual(ctx.eval("2*a"), 42) + expr = pya.Expression() res = expr.eval() self.assertEqual(str(type(res)).replace("class", "type"), "") diff --git a/testdata/ruby/dbLayoutQuery.rb b/testdata/ruby/dbLayoutQuery.rb index ec49d1c9c..dd10102b0 100644 --- a/testdata/ruby/dbLayoutQuery.rb +++ b/testdata/ruby/dbLayoutQuery.rb @@ -68,6 +68,37 @@ class DBLayoutQuery_TestClass < TestBase end + # variables + def test_4 + + ly = RBA::Layout::new + ly.read(ENV["TESTSRC"] + "/testdata/gds/t11.gds") + + ctx = RBA::ExpressionContext::new + ctx.var("suffix", "!") + ctx.var("all", []) + ctx.var("nonmod", "") + + q = RBA::LayoutQuery::new("select cell.name + suffix from *") + res = [] + q.each(ly, ctx) do |iter| + res << iter.data.inspect + end + + assert_equal(res.size, 2) + assert_equal(res[0], "[\"TOPTOP!\"]") + assert_equal(res[1], "[\"TOP!\"]") + + q = RBA::LayoutQuery::new("with * do var nonmod = cell.name; all.push(nonmod)") + q.execute(ly, ctx) + + assert_equal(ctx.eval("all").join(","), "TOPTOP,TOP") + # not modified, because we used "var nonmod" in the query which + # creates a local variable: + assert_equal(ctx.eval("nonmod"), "") + + end + end load("test_epilogue.rb") diff --git a/testdata/ruby/tlTest.rb b/testdata/ruby/tlTest.rb index 3874931f8..eb439b1bd 100644 --- a/testdata/ruby/tlTest.rb +++ b/testdata/ruby/tlTest.rb @@ -29,6 +29,11 @@ class Tl_TestClass < TestBase # Expression basics def test_1_Expression + ctx = RBA::ExpressionContext::new + assert_equal(ctx.eval("1+2"), 3) + ctx.var("a", 21) + assert_equal(ctx.eval("2*a"), 42) + expr = RBA::Expression::new res = expr.eval assert_equal(res.class.to_s, "NilClass")