diff --git a/src/db/db/dbLayoutQuery.cc b/src/db/db/dbLayoutQuery.cc index c5989b5bb..40835f186 100644 --- a/src/db/db/dbLayoutQuery.cc +++ b/src/db/db/dbLayoutQuery.cc @@ -2282,7 +2282,7 @@ private: // -------------------------------------------------------------------------------- // LayoutQueryIterator implementation -LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, tl::Eval *parent_eval, tl::AbsoluteProgress *progress) +LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, db::Cell *cell, tl::Eval *parent_eval, tl::AbsoluteProgress *progress) : mp_q (const_cast (&q)), mp_layout (layout), m_eval (parent_eval), m_layout_ctx (layout, true /*can modify*/), mp_progress (progress), m_initialized (false) { m_eval.set_ctx_handler (&m_layout_ctx); @@ -2290,13 +2290,16 @@ LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, db::Layout *layo for (unsigned int i = 0; i < mp_q->properties (); ++i) { m_eval.define_function (mp_q->property_name (i), new FilterStateFunction (i, &m_state)); } + if (cell && cell->layout ()) { + m_eval.set_var ("_", cell->layout ()->cell_name (cell->cell_index ())); + } // Avoid update() calls while iterating in modifying mode mp_layout->update (); mp_layout->start_changes (); } -LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, tl::Eval *parent_eval, tl::AbsoluteProgress *progress) +LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, const Cell *cell, tl::Eval *parent_eval, tl::AbsoluteProgress *progress) : mp_q (const_cast (&q)), mp_layout (const_cast (layout)), m_eval (parent_eval), m_layout_ctx (layout), mp_progress (progress), m_initialized (false) { // TODO: check whether the query is a modifying one (with .. do, delete) @@ -2306,6 +2309,9 @@ LayoutQueryIterator::LayoutQueryIterator (const LayoutQuery &q, const db::Layout for (unsigned int i = 0; i < mp_q->properties (); ++i) { m_eval.define_function (mp_q->property_name (i), new FilterStateFunction (i, &m_state)); } + if (cell && cell->layout ()) { + m_eval.set_var ("_", cell->layout ()->cell_name (cell->cell_index ())); + } // Avoid update() calls while iterating in modifying mode mp_layout->start_changes (); @@ -2850,9 +2856,9 @@ LayoutQuery::dump () const } void -LayoutQuery::execute (db::Layout &layout, tl::Eval *context) +LayoutQuery::execute (db::Layout &layout, db::Cell *cell, tl::Eval *context) { - LayoutQueryIterator iq (*this, &layout, context); + LayoutQueryIterator iq (*this, &layout, cell, context); while (! iq.at_end ()) { ++iq; } diff --git a/src/db/db/dbLayoutQuery.h b/src/db/db/dbLayoutQuery.h index a7ad97258..d67513e8c 100644 --- a/src/db/db/dbLayoutQuery.h +++ b/src/db/db/dbLayoutQuery.h @@ -536,7 +536,7 @@ public: * * The context provides a way to define variables and functions. */ - void execute (db::Layout &layout, tl::Eval *context = 0); + void execute (db::Layout &layout, db::Cell *cell = 0, tl::Eval *context = 0); /** * @brief A dump method (for debugging) @@ -578,7 +578,7 @@ public: * @param q The query that this iterator walks over * @param layout The layout to which the query is applied */ - LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0); + LayoutQueryIterator (const LayoutQuery &q, db::Layout *layout, db::Cell *cell = 0, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0); /** * @brief Constructor @@ -586,7 +586,7 @@ public: * @param q The query that this iterator walks over * @param layout The layout to which the query is applied */ - LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0); + LayoutQueryIterator (const LayoutQuery &q, const db::Layout *layout, const db::Cell *cell = 0, tl::Eval *parent_eval = 0, tl::AbsoluteProgress *progress = 0); /** * @brief Destructor diff --git a/src/db/db/gsiDeclDbLayoutQuery.cc b/src/db/db/gsiDeclDbLayoutQuery.cc index cf4f00d7d..d760b0f31 100644 --- a/src/db/db/gsiDeclDbLayoutQuery.cc +++ b/src/db/db/gsiDeclDbLayoutQuery.cc @@ -52,8 +52,8 @@ struct LayoutQueryIteratorWrapper typedef void difference_type; typedef void pointer; - LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout, tl::Eval *eval) - : mp_iter (new db::LayoutQueryIterator (q, layout, eval)) + LayoutQueryIteratorWrapper (const db::LayoutQuery &q, const db::Layout *layout, const db::Cell *cell, tl::Eval *eval) + : mp_iter (new db::LayoutQueryIterator (q, layout, cell, eval)) { // .. nothing yet .. } @@ -77,9 +77,14 @@ private: tl::shared_ptr mp_iter; }; -static LayoutQueryIteratorWrapper iterate (const db::LayoutQuery *q, const db::Layout *layout, tl::Eval *eval) +static LayoutQueryIteratorWrapper iterate1 (const db::LayoutQuery *q, const db::Layout *layout, tl::Eval *eval) { - return LayoutQueryIteratorWrapper (*q, layout, eval); + return LayoutQueryIteratorWrapper (*q, layout, 0, eval); +} + +static LayoutQueryIteratorWrapper iterate2 (const db::LayoutQuery *q, const db::Layout *layout, const db::Cell *cell, tl::Eval *eval) +{ + return LayoutQueryIteratorWrapper (*q, layout, cell, eval); } static tl::Variant iter_get (db::LayoutQueryIterator *iter, const std::string &name) @@ -158,6 +163,16 @@ Class decl_LayoutQueryIterator ("db", "LayoutQueryItera "The LayoutQueryIterator class has been introduced in version 0.25." ); +static void execute1 (db::LayoutQuery *q, db::Layout &layout, tl::Eval *context) +{ + q->execute (layout, 0, context); +} + +static void execute2 (db::LayoutQuery *q, db::Layout &layout, db::Cell *cell, tl::Eval *context) +{ + q->execute (layout, cell, context); +} + Class decl_LayoutQuery ("db", "LayoutQuery", gsi::constructor ("new", &new_query, gsi::arg ("query"), "@brief Creates a new query object from the given query string\n" @@ -168,7 +183,7 @@ 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::arg ("context", (tl::Eval *) 0, "nil"), + gsi::method_ext ("execute", &execute1, 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" @@ -179,13 +194,27 @@ Class decl_LayoutQuery ("db", "LayoutQuery", "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::arg ("context", (tl::Eval *) 0, "nil"), + gsi::method_ext ("execute", &execute2, gsi::arg("layout"), gsi::arg("cell"), gsi::arg ("context", (tl::Eval *) 0, "nil"), + "@brief Executes the query\n" + "\n" + "This version allows specifying a context cell. This cell can be used as a default cell for cell expressions.\n" + "\n" + "This variant has been introduced in version 0.30." + ) + + gsi::iterator_ext ("each", &iterate1, 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" + ) + + gsi::iterator_ext ("each", &iterate2, gsi::arg ("layout"), gsi::arg("cell"), gsi::arg ("context", (tl::Eval *) 0, "nil"), + "@brief Executes the query and delivered the results iteratively.\n" + "\n" + "This version allows specifying a context cell. This cell can be used as a default cell for cell expressions.\n" + "\n" + "This variant has been introduced in version 0.30." ), "@brief A layout query\n" "Layout queries are the backbone of the \"Search & replace\" feature. Layout queries allow retrieval of " diff --git a/src/db/unit_tests/dbLayoutQueryTests.cc b/src/db/unit_tests/dbLayoutQueryTests.cc index 79b059a57..a9ddb1102 100644 --- a/src/db/unit_tests/dbLayoutQueryTests.cc +++ b/src/db/unit_tests/dbLayoutQueryTests.cc @@ -515,6 +515,14 @@ TEST(1) EXPECT_EQ (s, "c1,c4,c5x"); } + { + // $_ is a placeholder for the current cell + db::LayoutQuery q ("$_.*"); + db::LayoutQueryIterator iq (q, &g, &g.cell (g.cell_by_name ("c4").second)); + std::string s = q2s_var (iq, "cell_name"); + EXPECT_EQ (s, "c1,c3"); // child cells of "c4" + } + { // Another way of saying "c2x.*" db::LayoutQuery q ("*.$(cell_name=='c2x'?'*':'')"); diff --git a/src/doc/doc/about/custom_queries.xml b/src/doc/doc/about/custom_queries.xml index 605c16457..62eec312e 100644 --- a/src/doc/doc/about/custom_queries.xml +++ b/src/doc/doc/about/custom_queries.xml @@ -281,6 +281,14 @@ select cell_name of cells TOP.. sorted by cell_name unique cells *.$("A"+cell_name) +

+ The "$_" placeholder is the name of the cell selected in the view as the current cell. + The following selects all child cells of the current cell: +

+ +
+cells $_.*
+

Building queries: instances

diff --git a/src/lay/lay/laySearchReplaceDialog.cc b/src/lay/lay/laySearchReplaceDialog.cc index 2e764a753..351b9aebd 100644 --- a/src/lay/lay/laySearchReplaceDialog.cc +++ b/src/lay/lay/laySearchReplaceDialog.cc @@ -1136,7 +1136,7 @@ BEGIN_PROTECTED progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query; @@ -1198,7 +1198,7 @@ BEGIN_PROTECTED progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query; @@ -1246,7 +1246,7 @@ BEGIN_PROTECTED progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query; @@ -1317,7 +1317,7 @@ BEGIN_PROTECTED progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query; @@ -1371,7 +1371,7 @@ BEGIN_PROTECTED progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << m_last_query; @@ -1668,7 +1668,7 @@ SearchReplaceDialog::issue_query (const std::string &q, const std::set * progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); while (! iq.at_end ()) { ++iq; } @@ -1690,7 +1690,7 @@ SearchReplaceDialog::issue_query (const std::string &q, const std::set * progress.set_format ("Processing .."); size_t n = 0; - for (db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); ! iq.at_end (); ++n) { + for (db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); ! iq.at_end (); ++n) { iq.next (selected_items->find (n) == selected_items->end ()); } @@ -1767,7 +1767,7 @@ SearchReplaceDialog::update_results (const std::string &q) progress.set_unit (100000); progress.set_format ("Processing .."); - db::LayoutQueryIterator iq (lq, &cv->layout (), 0, &progress); + db::LayoutQueryIterator iq (lq, &cv->layout (), cv.cell (), 0, &progress); if (tl::verbosity () >= 10) { tl::log << tl::to_string (QObject::tr ("Running query: ")) << q; diff --git a/src/layui/layui/layLayoutStatisticsForm.cc b/src/layui/layui/layLayoutStatisticsForm.cc index 583634840..6a4ca1d04 100644 --- a/src/layui/layui/layLayoutStatisticsForm.cc +++ b/src/layui/layui/layLayoutStatisticsForm.cc @@ -340,7 +340,7 @@ StatisticsTemplateProcessor::process (const QDomElement &element, tl::Eval &eval db::LayoutQuery q (tl::to_string (element.attribute (template_name_expr, template_value_empty_query))); - db::LayoutQueryIterator qi (q, mp_layout, &eval); + db::LayoutQueryIterator qi (q, mp_layout, 0, &eval); process_child_nodes (begin_node, qi.eval (), writer); diff --git a/src/tl/tl/tlExpression.cc b/src/tl/tl/tlExpression.cc index 679a85aaf..bb42ad65b 100644 --- a/src/tl/tl/tlExpression.cc +++ b/src/tl/tl/tlExpression.cc @@ -3692,10 +3692,41 @@ scan_angle_bracket (tl::Extractor &ex, const char *term, std::string &s) ex.expect (term); } +static bool +get_match_group (tl::Extractor &ex, int &group) +{ + tl::Extractor ex1 = ex; + group = 0; + if (ex1.test ("$") && isdigit (*ex1)) { + ex1.read (group); + ex = ex1; + return true; + } else { + return false; + } +} + +static bool +get_variable_name (tl::Extractor &ex, std::string &name) +{ + tl::Extractor ex1 = ex; + if (ex1.try_read_word (name, "_")) { + ex = ex1; + return true; + } else if (ex1.test ("$")) { + name = "$"; + ex = ex1; + return true; + } else { + return false; + } +} + void Eval::eval_atomic (ExpressionParserContext &ex, std::unique_ptr &n, int am) { double g = 0.0; + int match_group = 0; std::string t; ExpressionParserContext ex1 = ex; @@ -3792,12 +3823,10 @@ Eval::eval_atomic (ExpressionParserContext &ex, std::unique_ptr } - } else if (ex.test ("$")) { + } else if (get_match_group (ex, match_group)) { // match substring - int i = 0; - ex.read (i); - n.reset (new MatchSubstringReferenceNode (ex1, this, i - 1)); + n.reset (new MatchSubstringReferenceNode (ex1, this, match_group - 1)); } else if (ex.test ("{")) { @@ -3934,12 +3963,7 @@ Eval::eval_atomic (ExpressionParserContext &ex, std::unique_ptr n.reset (new ConstantExpressionNode (ex1, tl::Variant (t))); - } else if (ex.try_read_word (t, "_")) { - - ExpressionParserContext ex2 = ex; - - // for a function: collect the parameter or check if it's an assignment - std::vector vv; + } else if (get_variable_name (ex, t)) { const EvalFunction *function = 0; const tl::Variant *value = 0; diff --git a/src/tl/unit_tests/tlExpressionTests.cc b/src/tl/unit_tests/tlExpressionTests.cc index 8009071df..96083dc85 100644 --- a/src/tl/unit_tests/tlExpressionTests.cc +++ b/src/tl/unit_tests/tlExpressionTests.cc @@ -1084,9 +1084,14 @@ TEST(13) tl::Eval e, ee; e.set_var ("L", tl::Variant((long) 89)); ee.set_var ("L", tl::Variant((long) 123)); + ee.set_var ("$", tl::Variant("dollar")); + ee.set_var ("_", tl::Variant("underscore")); - EXPECT_EQ (e.interpolate("A$L B$(L+100)C"), std::string ("A89 B189C")); - EXPECT_EQ (ee.interpolate("123*11=$(L*11)."), std::string ("123*11=1353.")); + EXPECT_EQ (e.interpolate ("A$L B$(L+100)C"), std::string ("A89 B189C")); + EXPECT_EQ (ee.interpolate ("123*11=$(L*11)."), std::string ("123*11=1353.")); + EXPECT_EQ (ee.interpolate ("A$$C"), std::string ("A$C")); + EXPECT_EQ (ee.interpolate ("A$_ C"), std::string ("Aunderscore C")); + EXPECT_EQ (ee.interpolate ("A$($)C"), std::string ("AdollarC")); } // assignment