Layout queries support diff as placeholder for the current cell

This commit is contained in:
Matthias Koefferlein 2025-03-23 16:51:47 +01:00
parent a22f48d87a
commit efeb2c061b
9 changed files with 114 additions and 34 deletions

View File

@ -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<db::LayoutQuery *> (&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<db::LayoutQuery *> (&q)), mp_layout (const_cast <db::Layout *> (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;
}

View File

@ -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

View File

@ -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<db::LayoutQueryIterator> 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<db::LayoutQueryIterator> 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<db::LayoutQuery> 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<db::LayoutQuery> 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<db::LayoutQuery> 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 "

View File

@ -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'?'*':'')");

View File

@ -281,6 +281,14 @@ select cell_name of cells TOP.. sorted by cell_name unique
cells *.$("A"+cell_name)
</pre>
<p>
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:
</p>
<pre>
cells $_.*
</pre>
<h2>Building queries: instances</h2>

View File

@ -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<size_t> *
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<size_t> *
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;

View File

@ -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);

View File

@ -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<ExpressionNode> &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<ExpressionNode>
}
} 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<ExpressionNode>
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 <tl::Variant> vv;
} else if (get_variable_name (ex, t)) {
const EvalFunction *function = 0;
const tl::Variant *value = 0;

View File

@ -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