/* KLayout Layout Viewer Copyright (C) 2006-2017 Matthias Koefferlein This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "layParsedLayerSource.h" #include "layLayoutView.h" #include "tlString.h" #include "tlGlobPattern.h" #include namespace lay { static PropertySelectorBase *extract_top (tl::Extractor &ex); // -------------------------------------------------------------------------- // PropertySelectorBase definition & implementation /** * @brief A base class for the expression graph nodes */ class PropertySelectorBase { public: PropertySelectorBase () { } virtual ~PropertySelectorBase () { } virtual std::string to_string (bool inner, size_t max_len) const = 0; virtual PropertySelectorBase *clone () const = 0; virtual int compare (const PropertySelectorBase *b) const = 0; virtual unsigned int type_id () const = 0; virtual bool check (const db::PropertiesRepository &rep, const db::PropertiesRepository::properties_set &set) const = 0; virtual bool selection (const db::PropertiesRepository &rep, std::set &ids) const = 0; }; /** * @brief A expression graph node combining n arguments with either a "and" or "or" operation */ class PropertySelectorOp : public PropertySelectorBase { public: enum op_type_enum { And, Or }; PropertySelectorOp (op_type_enum op, const PropertySelectorBase *arg) : m_op (op) { m_args.push_back (arg); } PropertySelectorOp (const PropertySelectorOp &d) : m_op (d.m_op) { m_args.reserve (d.m_args.size ()); for (std::vector::const_iterator b = d.m_args.begin (); b != d.m_args.end (); ++b) { m_args.push_back ((*b)->clone ()); } } ~PropertySelectorOp () { for (std::vector::iterator b = m_args.begin (); b != m_args.end (); ++b) { delete const_cast (*b); } m_args.clear (); } virtual std::string to_string (bool inner, size_t max_len) const { std::string s; if (inner) { s += "("; } s += m_args [0]->to_string (true, max_len); for (std::vector::const_iterator b = m_args.begin () + 1; b != m_args.end (); ++b) { s += (m_op == And) ? "&&" : "||"; if (s.size () > max_len) { s += "..."; break; } s += (*b)->to_string (true, max_len); } if (inner) { s += ")"; } return s; } void add_arg (const PropertySelectorBase *arg) { m_args.push_back (arg); } PropertySelectorBase *clone () const { return new PropertySelectorOp (*this); } void join (const PropertySelectorBase *other) { const PropertySelectorOp *op = dynamic_cast (other); if (op && op->op_type () == And) { m_args.reserve (m_args.size () + op->m_args.size ()); for (std::vector::const_iterator b = op->m_args.begin (); b != op->m_args.end (); ++b) { m_args.push_back ((*b)->clone ()); } } else { m_args.push_back (other); } } op_type_enum op_type () const { return m_op; } bool check (const db::PropertiesRepository &rep, const db::PropertiesRepository::properties_set &set) const { if (m_op == And) { for (std::vector::const_iterator b = m_args.begin (); b != m_args.end (); ++b) { if (! (*b)->check (rep, set)) { return false; } } return true; } else { for (std::vector::const_iterator b = m_args.begin (); b != m_args.end (); ++b) { if ((*b)->check (rep, set)) { return true; } } return false; } } bool selection (const db::PropertiesRepository &rep, std::set &ids) const { // this algorithm computes the "or" of two sets by using this relationship: a or b or c or .. = !((!a) and (!b) and (!c) and ..) // get the selection of the first operand into ids std::vector::const_iterator b = m_args.begin (); bool inv = (*b)->selection (rep, ids); if (m_op == Or) { inv = !inv; } for (++b; b != m_args.end () && !(ids.empty () && !inv); ++b) { // get the selection of the next operand into ids2 std::set ids2; bool inv2 = (*b)->selection (rep, ids2); if (m_op == Or) { inv2 = !inv2; } // compute the intersection of ids and ids2 in place int ids if (ids2.empty () && !inv2) { // shortcut: if the second operand is empty, just clear and terminate the loop then ids.clear (); inv = false; } else if (!inv && !inv2) { for (std::set::iterator id = ids.begin (); id != ids.end (); ) { std::set::iterator i = id; ++id; if (ids2.find (*i) == ids2.end ()) { ids.erase (i); } } } else if (inv && inv2) { for (std::set::iterator id = ids2.begin (); id != ids2.end (); ++id) { ids.insert (*id); } } else { // swap current and new ids such that inv==false if (inv) { std::swap (inv, inv2); ids.swap (ids2); } // from ids subtract all ids that are in ids2 (inv2==true!) for (std::set::iterator id = ids.begin (); id != ids.end (); ) { std::set::iterator i = id; ++id; if (ids2.find (*i) != ids2.end ()) { ids.erase (i); } } } } return m_op == Or ? !inv : inv; } unsigned int type_id () const { return m_op == And ? 1 : 2; } int compare (const PropertySelectorBase *b) const { if (type_id () != b->type_id ()) { return type_id () < b->type_id () ? -1 : 1; } const PropertySelectorOp *bb = dynamic_cast (b); if (bb) { if (m_args.size () != bb->m_args.size ()) { return (m_args.size () < bb->m_args.size ()) ? -1 : 1; } for (size_t n = 0; n < m_args.size (); ++n) { int cmp = m_args[n]->compare (bb->m_args[n]); if (cmp != 0) { return cmp; } } } return 0; } private: op_type_enum m_op; std::vector m_args; }; /** * @brief A expression graph node forming the inverse of one argument */ class PropertySelectorNot : public PropertySelectorBase { public: PropertySelectorNot (PropertySelectorBase *arg) : mp_arg (arg) { // .. nothing yet .. } ~PropertySelectorNot () { delete mp_arg; mp_arg = 0; } virtual std::string to_string (bool /*inner*/, size_t max_len) const { return "!(" + mp_arg->to_string (false, max_len) + ")"; } PropertySelectorBase *clone () const { return new PropertySelectorNot (mp_arg->clone ()); } bool check (const db::PropertiesRepository &rep, const db::PropertiesRepository::properties_set &set) const { return ! mp_arg->check (rep, set); } bool selection (const db::PropertiesRepository &rep, std::set &ids) const { return ! mp_arg->selection (rep, ids); } unsigned int type_id () const { return 10; } int compare (const PropertySelectorBase *b) const { if (type_id () != b->type_id ()) { return type_id () < b->type_id () ? -1 : 1; } const PropertySelectorNot *bb = dynamic_cast (b); return bb ? mp_arg->compare (bb->mp_arg) : 0; } private: PropertySelectorBase *mp_arg; }; /** * @brief A expression graph leaf node: a comparison operation */ class PropertySelectorEqual : public PropertySelectorBase { public: PropertySelectorEqual (const tl::Variant &name, const tl::Variant &value, bool equal) : m_name (name), m_value (value), m_equal (equal) { // .. nothing yet .. } ~PropertySelectorEqual () { // .. nothing yet .. } virtual std::string to_string (bool /*inner*/, size_t /*max_len*/) const { std::string s = m_name.to_parsable_string (); if (m_equal) { s += "=="; } else { s += "!="; } s += m_value.to_parsable_string (); return s; } PropertySelectorBase *clone () const { return new PropertySelectorEqual (m_name, m_value, m_equal); } bool check (const db::PropertiesRepository &rep, const db::PropertiesRepository::properties_set &set) const { std::pair p = rep.get_id_of_name (m_name); if (! p.first) { // name is not known at all. return false; } db::PropertiesRepository::properties_set::const_iterator i = set.find (p.second); if (i == set.end ()) { // name is not present in the property set return false; } else { // check value if (m_equal && i->second == m_value) { return true; } else if (! m_equal && i->second != m_value) { return true; } else { return false; } } } bool selection (const db::PropertiesRepository &rep, std::set &ids) const { std::pair p = rep.get_id_of_name (m_name); if (! p.first) { // name is not known at all. return false; } const db::PropertiesRepository::properties_id_vector &idv = rep.properties_ids_by_name_value (std::make_pair (p.second, m_value)); for (db::PropertiesRepository::properties_id_vector::const_iterator id = idv.begin (); id != idv.end (); ++id) { ids.insert (*id); } return ! m_equal; } unsigned int type_id () const { return m_equal ? 20 : 21; } int compare (const PropertySelectorBase *b) const { if (type_id () != b->type_id ()) { return type_id () < b->type_id () ? -1 : 1; } const PropertySelectorEqual *bb = dynamic_cast (b); if (bb) { if (m_name != bb->m_name) { return m_name < bb->m_name ? -1 : 1; } if (m_value != bb->m_value) { return m_value < bb->m_value ? -1 : 1; } } return 0; } private: tl::Variant m_name, m_value; bool m_equal; }; /** * @brief Expression parser: extract one comparison operation */ static PropertySelectorBase * extract_base (tl::Extractor &ex) { bool eq = true; tl::Variant n, v; ex.read (n); if (ex.test ("==")) { eq = true; } else if (ex.test ("!=")) { eq = false; } else { ex.error (tl::to_string (QObject::tr ("'==' or '!=' operator expected"))); } ex.read (v); return new PropertySelectorEqual (n, v, eq); } /** * @brief Expression parser: parse complex elements (brackted expressions) */ static PropertySelectorBase * extract_element (tl::Extractor &ex) { if (ex.test ("(")) { PropertySelectorBase *expr = extract_top (ex); ex.expect (")"); return expr; } else { return extract_base (ex); } } /** * @brief Expression parser: parse unary operations with one argument */ static PropertySelectorBase * extract_unary (tl::Extractor &ex) { if (ex.test ("!")) { return new PropertySelectorNot (extract_unary (ex)); } else { return extract_element (ex); } } /** * @brief Expression parser: parse binary operations at level 2 */ static PropertySelectorBase * extract_or_seq (tl::Extractor &ex) { PropertySelectorBase *expr = extract_unary (ex); if (ex.test ("||")) { PropertySelectorOp *op = new PropertySelectorOp (PropertySelectorOp::Or, expr); expr = op; do { op->add_arg (extract_unary (ex)); } while (ex.test ("||")); } return expr; } /** * @brief Expression parser: parse binary operations at level 1 */ static PropertySelectorBase * extract_and_seq (tl::Extractor &ex) { PropertySelectorBase *expr = extract_or_seq (ex); if (ex.test ("&&")) { PropertySelectorOp *op = new PropertySelectorOp (PropertySelectorOp::And, expr); expr = op; do { op->add_arg (extract_or_seq (ex)); } while (ex.test ("&&")); } return expr; } /** * @brief Expression parser: parse top level expressions */ static PropertySelectorBase * extract_top (tl::Extractor &ex) { return extract_and_seq (ex); } // -------------------------------------------------------------------------- // PropertySelector implementation PropertySelector::PropertySelector () : mp_base (0) { // .. nothing yet .. } PropertySelector::PropertySelector (const PropertySelector &sel) : mp_base (0) { operator= (sel); } PropertySelector & PropertySelector::operator= (const PropertySelector &sel) { if (this != &sel) { if (mp_base) { delete mp_base; mp_base = 0; } if (sel.mp_base) { mp_base = sel.mp_base->clone (); } } return *this; } bool PropertySelector::operator== (const PropertySelector &sel) const { if (mp_base == 0 && sel.mp_base == 0) { return true; } else if (mp_base && sel.mp_base) { return mp_base->compare (sel.mp_base) == 0; } else { return false; } } bool PropertySelector::operator< (const PropertySelector &sel) const { if (mp_base == 0 && sel.mp_base == 0) { return false; } else if (mp_base && sel.mp_base) { return mp_base->compare (sel.mp_base) < 0; } else { return mp_base == 0; } } PropertySelector::~PropertySelector () { if (mp_base) { delete mp_base; } mp_base = 0; } void PropertySelector::extract (tl::Extractor &ex) { if (mp_base) { delete mp_base; } mp_base = extract_top (ex); } std::string PropertySelector::to_string (size_t max_len) const { if (mp_base) { return mp_base->to_string (false, max_len); } else { return std::string (); } } void PropertySelector::join (const PropertySelector &d) { // Create a combined "and" operator of both property selectors if (d.mp_base) { if (! mp_base) { mp_base = d.mp_base->clone (); } else { PropertySelectorOp *op = dynamic_cast (mp_base); if (op && op->op_type () == PropertySelectorOp::And) { op->join (d.mp_base); } else { op = new PropertySelectorOp (PropertySelectorOp::And, mp_base); op->add_arg (d.mp_base->clone ()); mp_base = op; } } } } bool PropertySelector::check (const db::PropertiesRepository &rep, db::properties_id_type id) const { if (is_null ()) { return true; } else { return mp_base->check (rep, rep.properties (id)); } } bool PropertySelector::matching (const db::PropertiesRepository &rep, std::set &ids) const { if (is_null ()) { return true; } else { return mp_base->selection (rep, ids); } } // -------------------------------------------------------------------------- // PartialTreeSelector implementation PartialTreeSelector::PartialTreeSelector () : mp_layout (0), m_state (0), m_selected (false) { // .. nothing yet .. } PartialTreeSelector::PartialTreeSelector (const db::Layout &layout, bool initially_selected) : mp_layout (&layout), m_state (0), m_selected (initially_selected) { // .. nothing yet .. } PartialTreeSelector::PartialTreeSelector (const PartialTreeSelector &d) : mp_layout (d.mp_layout), m_state (d.m_state), m_selected (d.m_selected), m_state_stack (d.m_state_stack), m_selected_stack (d.m_selected_stack), m_state_machine (d.m_state_machine) { // .. nothing yet .. } PartialTreeSelector &PartialTreeSelector::operator= (const PartialTreeSelector &d) { if (this != &d) { mp_layout = d.mp_layout; m_state = d.m_state; m_selected = d.m_selected; m_state_stack = d.m_state_stack; m_selected_stack = d.m_selected_stack; m_state_machine = d.m_state_machine; } return *this; } int PartialTreeSelector::is_child_selected (db::cell_index_type child) const { if (m_state >= 0 && m_state < int (m_state_machine.size ())) { const std::map > &m = m_state_machine [m_state]; std::map >::const_iterator i = m.find (child); if (i == m.end ()) { i = m.find (db::cell_index_type (-1)); } if (i != m.end ()) { bool sel = i->second.second >= 0 ? i->second.second : m_selected; if (i->second.first < 0 || i->second.first >= int (m_state_machine.size ())) { return sel ? 1 : 0; } else { return sel ? 1 : -1; } } } return m_selected ? 1 : 0; } void PartialTreeSelector::descend (db::cell_index_type child) { if (m_state_machine.empty ()) { return; } m_state_stack.push_back (m_state); m_selected_stack.push_back (m_selected); if (m_state >= 0 && m_state < int (m_state_machine.size ())) { const std::map > &m = m_state_machine [m_state]; std::map >::const_iterator i = m.find (child); if (i == m.end ()) { i = m.find (db::cell_index_type (-1)); } if (i != m.end ()) { m_state = i->second.first; if (i->second.second >= 0) { m_selected = i->second.second; } } } } void PartialTreeSelector::ascend () { if (m_state_machine.empty ()) { return; } if (! m_state_stack.empty ()) { m_state = m_state_stack.back (); m_state_stack.pop_back (); m_selected = m_selected_stack.back (); m_selected_stack.pop_back (); } } void PartialTreeSelector::add_state_transition (int initial_state, db::cell_index_type cell_index, int target_state, int selected) { if (initial_state >= 0) { while (int (m_state_machine.size ()) <= initial_state) { m_state_machine.push_back (std::map > ()); } m_state_machine [initial_state][cell_index] = std::make_pair (target_state, selected); } } void PartialTreeSelector::add_state_transition (int initial_state, int target_state, int selected) { if (initial_state >= 0) { while (int (m_state_machine.size ()) <= initial_state) { m_state_machine.push_back (std::map > ()); } m_state_machine [initial_state].clear (); m_state_machine [initial_state][db::cell_index_type (-1)] = std::make_pair (target_state, selected); } } // -------------------------------------------------------------------------- // CellSelector implementation CellSelector::CellSelector () { // .. nothing yet .. } CellSelector::CellSelector (const CellSelector &d) : m_selectors (d.m_selectors) { // .. nothing yet .. } CellSelector &CellSelector::operator= (const CellSelector &d) { if (this != &d) { m_selectors = d.m_selectors; } return *this; } bool CellSelector::operator== (const CellSelector &d) const { return m_selectors == d.m_selectors; } bool CellSelector::operator< (const CellSelector &d) const { return m_selectors < d.m_selectors; } static std::pair parse_part (tl::Extractor &ex) { bool sel = true; if (ex.test ("-")) { sel = false; } else if (ex.test ("+")) { sel = true; } std::string nf; if (ex.try_read_word_or_quoted (nf, "_.$*?[]")) { return std::make_pair (sel, nf); } else { return std::pair (); } } static std::vector > parse_list (tl::Extractor &ex) { std::vector > list; if (ex.test ("(")) { while (! ex.test (")")) { list.push_back (parse_part (ex)); if (list.back () == std::pair ()) { list.pop_back (); ex.expect (")"); break; } } } else { list.push_back (parse_part (ex)); if (list.back () == std::pair ()) { list.pop_back (); } } return list; } void CellSelector::parse (tl::Extractor &ex) { m_selectors.clear (); while (! ex.at_end ()) { m_selectors.push_back (parse_list (ex)); if (m_selectors.back ().empty ()) { m_selectors.pop_back (); break; } } } std::string CellSelector::to_string () const { std::string r; for (std::vector > >::const_iterator i = m_selectors.begin (); i != m_selectors.end (); ++i) { if (! r.empty ()) { r += " "; } if (i->size () > 1) { r += "("; } for (std::vector >::const_iterator j = i->begin (); j != i->end (); ++j) { if (j != i->begin ()) { r += " "; } r += j->first ? "+" : "-"; r += tl::to_word_or_quoted_string (j->second, "_.$*?[]"); } if (i->size () > 1) { r += ")"; } } return r; } PartialTreeSelector CellSelector::create_tree_selector (const db::Layout &layout, db::cell_index_type initial_cell) const { // start in deselected state if the front selector is selecting ("+ABC") bool initial_sel = true; if (! m_selectors.empty () && ! m_selectors.front ().empty () && m_selectors.front ().front ().first) { initial_sel = false; } // if the first level matches the initial cell, use the selection state to enable this cell bool consume_first = false; if (! m_selectors.empty () && layout.is_valid_cell_index (initial_cell)) { for (std::vector >::const_iterator j = m_selectors.front ().begin (); j != m_selectors.front ().end (); ++j) { tl::GlobPattern pat (j->second); if (pat.match (layout.cell_name (initial_cell))) { initial_sel = j->first; consume_first = true; } } } PartialTreeSelector pts (layout, initial_sel); int state = 0; for (std::vector > >::const_iterator i = m_selectors.begin (); i != m_selectors.end (); ++i) { // The first level is consumed by the initial cell if (i == m_selectors.begin () && consume_first) { continue; } // default loop for any other cell pts.add_state_transition (state, state, -1); for (std::vector >::const_iterator j = i->begin (); j != i->end (); ++j) { if (j->second == "*") { // global select/deselect pts.add_state_transition (state, state + 1, j->first ? 1 : 0); } else { // named select/deselect tl::GlobPattern pat (j->second); for (db::cell_index_type ci = 0; ci < layout.cells (); ++ci) { if (layout.is_valid_cell_index (ci) && pat.match (layout.cell_name (ci))) { pts.add_state_transition (state, ci, state + 1, j->first ? 1 : 0); } } } } ++state; } return pts; } // -------------------------------------------------------------------------- // ParsedLayerSource implementation ParsedLayerSource::ParsedLayerSource (const std::string &src) : m_has_name (false), m_special_purpose (SP_None), m_layer_index (-1), m_layer (-1), m_datatype (-1), m_cv_index (-1) { parse_from_string (src.c_str ()); } ParsedLayerSource::ParsedLayerSource (const ParsedLayerSource &d) : m_has_name (false), m_special_purpose (SP_None), m_layer_index (-1), m_layer (-1), m_datatype (-1), m_cv_index (-1) { operator= (d); } ParsedLayerSource::ParsedLayerSource (const db::LayerProperties &lp, int cv_index) : m_has_name (! lp.name.empty ()), m_special_purpose (SP_None), m_layer_index (-1), m_layer (lp.layer), m_datatype (lp.datatype), m_name (lp.name), m_cv_index (cv_index) { m_trans.push_back (db::DCplxTrans ()); } ParsedLayerSource::ParsedLayerSource (int layer, int datatype, int cv_index) : m_has_name (false), m_special_purpose (SP_None), m_layer_index (-1), m_layer (layer), m_datatype (datatype), m_cv_index (cv_index) { m_trans.push_back (db::DCplxTrans ()); } ParsedLayerSource::ParsedLayerSource (int layer_index, int cv_index) : m_has_name (false), m_special_purpose (SP_None), m_layer_index (layer_index), m_layer (-1), m_datatype (-1), m_cv_index (cv_index) { m_trans.push_back (db::DCplxTrans ()); } ParsedLayerSource::ParsedLayerSource (const std::string &name, int cv_index) : m_has_name (true), m_special_purpose (SP_None), m_layer_index (-1), m_layer (-1), m_datatype (-1), m_name (name), m_cv_index (cv_index) { m_trans.push_back (db::DCplxTrans ()); } ParsedLayerSource::ParsedLayerSource () : m_has_name (false), m_special_purpose (SP_None), m_layer_index (-1), m_layer (-1), m_datatype (-1), m_cv_index (-1) { m_trans.push_back (db::DCplxTrans ()); } ParsedLayerSource & ParsedLayerSource::operator= (const ParsedLayerSource &d) { if (this != &d) { m_has_name = d.m_has_name; m_special_purpose = d.m_special_purpose; m_layer_index = d.m_layer_index; m_layer = d.m_layer; m_datatype = d.m_datatype; m_name = d.m_name; m_cv_index = d.m_cv_index; m_trans = d.m_trans; m_property_sel = d.m_property_sel; m_cell_sel = d.m_cell_sel; m_hier_levels = d.m_hier_levels; } return *this; } ParsedLayerSource & ParsedLayerSource::operator+= (const ParsedLayerSource &d) { if (m_layer_index < 0) { m_layer_index = d.m_layer_index; } // attempt a mixture of ours and the other's properties if (m_special_purpose == SP_None) { m_special_purpose = d.m_special_purpose; } if (m_layer < 0) { m_layer = d.m_layer; } if (m_datatype < 0) { m_datatype = d.m_datatype; } if (! m_has_name) { m_name = d.m_name; m_has_name = d.m_has_name; } if (m_cv_index < 0) { m_cv_index = d.m_cv_index; } if (m_cell_sel.is_empty ()) { m_cell_sel = d.m_cell_sel; } m_property_sel.join (d.m_property_sel); std::vector tr; tr.reserve (m_trans.size () * d.m_trans.size ()); for (std::vector::const_iterator a = m_trans.begin (); a != m_trans.end (); ++a) { for (std::vector::const_iterator b = d.m_trans.begin (); b != d.m_trans.end (); ++b) { tr.push_back (*a * *b); } } m_trans.swap (tr); m_hier_levels = m_hier_levels.combine (d.m_hier_levels); return *this; } static std::string hier_levels_to_string (const HierarchyLevelSelection &hier_levels) { std::string r; if (hier_levels.has_from_level ()) { std::string m; if (hier_levels.from_level_mode () == HierarchyLevelSelection::minimum) { m = "<"; } else if (hier_levels.from_level_mode () == HierarchyLevelSelection::maximum) { m = ">"; } if (hier_levels.from_level_relative ()) { r += "(" + m + tl::to_string (hier_levels.from_level ()) + ")"; } else { r += m + tl::to_string (hier_levels.from_level ()); } } r += ".."; if (hier_levels.has_to_level ()) { std::string m; if (hier_levels.to_level_mode () == HierarchyLevelSelection::minimum) { m = "<"; } else if (hier_levels.to_level_mode () == HierarchyLevelSelection::maximum) { m = ">"; } if (hier_levels.to_level () == std::numeric_limits::max ()) { r += m + "*"; } else if (hier_levels.to_level_relative ()) { r += "(" + m + tl::to_string (hier_levels.to_level ()) + ")"; } else { r += m + tl::to_string (hier_levels.to_level ()); } } return r; } std::string ParsedLayerSource::to_string () const { std::string r; if (m_layer_index >= 0) { if (! r.empty ()) { r += " "; } r += tl::sprintf ("%%%d", m_layer_index); } else { // the normal source specification is either // - name specification only // / - layer/datatype // / - name plus layer/datatype if (m_has_name) { if (! r.empty ()) { r += " "; } r += tl::to_word_or_quoted_string (m_name); } else if (m_layer < 0 && m_datatype < 0 && m_special_purpose == SP_None) { if (! r.empty ()) { r += " "; } r += "*/*"; } if (m_layer >= 0 || m_datatype >= 0) { if (! r.empty ()) { r += " "; } if (m_layer < 0) { r += tl::sprintf ("*/%d", m_datatype); } else if (m_datatype < 0) { r += tl::sprintf ("%d/*", m_layer); } else { r += tl::sprintf ("%d/%d", m_layer, m_datatype); } } } if (m_cv_index >= 0) { r += tl::sprintf ("@%d", m_cv_index + 1); } else { r += "@*"; } switch (m_special_purpose) { case SP_None: break; case SP_CellFrame: if (! r.empty ()) { r += " "; } r += "!CellFrame"; break; } if (! m_cell_sel.is_empty ()) { if (! r.empty ()) { r += " "; } r += "{"; r += m_cell_sel.to_string (); r += "}"; } if (! m_trans.empty () && (m_trans.size () > 1 || m_trans[0] != db::DCplxTrans ())) { for (std::vector::const_iterator t = m_trans.begin (); t != m_trans.end (); ++t) { if (! r.empty ()) { r += " "; } r += "("; r += t->to_string (); r += ")"; } } if (! m_property_sel.is_null ()) { if (! r.empty ()) { r += " "; } r += "[" + m_property_sel.to_string (std::numeric_limits::max ()) + "]"; } if (m_hier_levels.has_from_level () || m_hier_levels.has_to_level ()) { if (! r.empty ()) { r += " "; } r += "#" + hier_levels_to_string (m_hier_levels); } return r; } std::string ParsedLayerSource::display_string (const lay::LayoutView *view) const { std::string r; if (m_layer_index >= 0) { if (!view || m_cv_index < 0 || m_cv_index >= int (view->cellviews ()) || ! view->cellview (m_cv_index)->layout ().is_valid_layer (m_layer_index)) { r = tl::sprintf ("%%%d", m_layer_index); } else { const db::LayerProperties &lp = view->cellview (m_cv_index)->layout ().get_properties (m_layer_index); if (! lp.name.empty ()) { r = lp.name; if (lp.layer >= 0 && lp.datatype >= 0 && view->always_show_ld ()) { r += tl::sprintf (" %d/%d", lp.layer, lp.datatype); } } else { if (lp.layer < 0 && lp.datatype < 0) { r = tl::sprintf ("%%%d", m_layer_index); } else if (lp.layer < 0) { r = tl::sprintf ("*/%d", lp.datatype); } else if (lp.datatype < 0) { r = tl::sprintf ("%d/*", lp.layer); } else { r = tl::sprintf ("%d/%d", lp.layer, lp.datatype); } } } } else if (m_has_name) { r = m_name; if (m_layer >= 0 && m_datatype >= 0 && (!view || view->always_show_ld ())) { r += tl::sprintf (" %d/%d", m_layer, m_datatype); } } else { if (m_layer < 0 && m_datatype < 0) { r = ""; } else if (m_layer < 0) { r = tl::sprintf ("*/%d", m_datatype); } else if (m_datatype < 0) { r = tl::sprintf ("%d/*", m_layer); } else { r = tl::sprintf ("%d/%d", m_layer, m_datatype); } } if (m_cv_index >= 0 && (!view || view->always_show_layout_index () || m_cv_index > 0 || view->cellviews () > 1)) { r += tl::sprintf ("@%d", m_cv_index + 1); } switch (m_special_purpose) { case SP_None: break; case SP_CellFrame: if (! r.empty ()) { r += " "; } r += "!CellFrame"; break; } if (! m_cell_sel.is_empty ()) { if (! r.empty ()) { r += " "; } r += m_cell_sel.to_string (); } if (! m_trans.empty () && (m_trans.size () > 1 || m_trans[0] != db::DCplxTrans ())) { for (std::vector::const_iterator t = m_trans.begin (); t != m_trans.end (); ++t) { if (! r.empty ()) { r += " "; } r += "("; r += t->to_string (); r += ")"; } } if (! m_property_sel.is_null ()) { if (! r.empty ()) { r += " "; } r += "[" + m_property_sel.to_string (32) + "]"; } if (m_hier_levels.has_from_level () || m_hier_levels.has_to_level ()) { if (! r.empty ()) { r += " "; } r += "#" + hier_levels_to_string (m_hier_levels); } return r; } bool ParsedLayerSource::operator== (const ParsedLayerSource &d) const { if (m_trans != d.m_trans) { return false; } if (m_cell_sel != d.m_cell_sel) { return false; } if (m_property_sel != d.m_property_sel) { return false; } if (m_cv_index != d.m_cv_index) { return false; } if (m_hier_levels != d.m_hier_levels) { return false; } if (m_has_name != d.m_has_name) { return false; } if (m_special_purpose != d.m_special_purpose) { return false; } if (m_layer_index != d.m_layer_index) { return false; } if ((m_layer < 0) != (d.m_layer < 0)) { return false; } if (m_layer >= 0 && m_layer != d.m_layer) { return false; } if ((m_datatype < 0) != (d.m_datatype < 0)) { return false; } if (m_datatype >= 0 && m_datatype != d.m_datatype) { return false; } if (m_has_name && m_name != d.m_name) { return false; } return true; } bool ParsedLayerSource::operator< (const ParsedLayerSource &d) const { if (m_trans != d.m_trans) { return m_trans < d.m_trans; } if (m_cell_sel != d.m_cell_sel) { return m_cell_sel < d.m_cell_sel; } if (m_property_sel != d.m_property_sel) { return m_property_sel < d.m_property_sel; } if (m_cv_index != d.m_cv_index) { return m_cv_index < d.m_cv_index; } if (m_hier_levels != d.m_hier_levels) { return m_hier_levels < d.m_hier_levels; } if (m_special_purpose != d.m_special_purpose) { return m_special_purpose < d.m_special_purpose; } if (m_layer_index != d.m_layer_index) { return m_layer_index < d.m_layer_index; } bool is_named = (m_layer < 0 && m_datatype < 0); bool d_is_named = (d.m_layer < 0 && d.m_datatype < 0); if (is_named != d_is_named) { return is_named < d_is_named; } if (is_named) { if (m_has_name != d.m_has_name) { return m_has_name < d.m_has_name; } if (m_has_name) { return m_name < d.m_name; } else { return false; } } else { if (m_layer != d.m_layer) { return m_layer < d.m_layer; } return m_datatype < d.m_datatype; } } void ParsedLayerSource::parse_from_string (const char *cp) { m_layer_index = -1; m_special_purpose = SP_None; m_layer = -1; m_datatype = -1; m_name = ""; m_cv_index = 0; m_has_name = false; m_trans.clear (); m_cell_sel = CellSelector (); unsigned int v = 0; tl::Extractor x (cp); while (! x.at_end ()) { if (x.test ("!")) { std::string sp; x.read (sp); if (sp == "CellFrame" || sp == "cellframe" || sp == "CF" || sp == "cell-frame") { m_special_purpose = SP_CellFrame; } else { throw tl::Exception (tl::to_string (QObject::tr ("Invalid special purpose '%s'")), sp); } } else if (x.test ("(")) { db::DCplxTrans t; x.read (t); x.expect (")"); m_trans.push_back (t); } else if (x.test ("[")) { m_property_sel.extract (x); x.expect ("]"); } else if (x.test ("%")) { unsigned int n; x.read (n); m_layer_index = int (n); } else if (x.test ("#")) { // The hierarchy level selection format is that (C=number of context levels, above current cell) // #n -> = #0..n // #(n) -> = #(0)..(n) // #..n -> = #0..n // #..(n) -> = #0..(n) // #m..n -> from=m, to=n // #(m)..(n) -> from=C+m, to=C+n // #m..(n) -> from=m, to=C+n // #(m)..n -> from=C+m, to=n // m and n can be "l" to indicate minimum or maximum mode respectively m_hier_levels = HierarchyLevelSelection (); if (x.test ("*")) { m_hier_levels.set_from_level (0, false, HierarchyLevelSelection::absolute); m_hier_levels.set_to_level (std::numeric_limits::max (), false, HierarchyLevelSelection::absolute); } else if (x.test ("<*")) { m_hier_levels.set_from_level (0, false, HierarchyLevelSelection::absolute); m_hier_levels.set_to_level (std::numeric_limits::max (), false, HierarchyLevelSelection::minimum); } else if (x.test ("(*)")) { m_hier_levels.set_from_level (0, true, HierarchyLevelSelection::absolute); m_hier_levels.set_to_level (std::numeric_limits::max (), false, HierarchyLevelSelection::absolute); } else if (x.test ("(<*)")) { m_hier_levels.set_from_level (0, true, HierarchyLevelSelection::absolute); m_hier_levels.set_to_level (std::numeric_limits::max (), false, HierarchyLevelSelection::minimum); } else { HierarchyLevelSelection::level_mode_type m; int f = 0; m = HierarchyLevelSelection::absolute; if (x.test ("(")) { if (x.test ("<")) { m = HierarchyLevelSelection::minimum; } else if (x.test (">")) { m = HierarchyLevelSelection::maximum; } x.read (f); x.expect(")"); m_hier_levels.set_from_level (f, true, m); } else { if (x.test ("<")) { m = HierarchyLevelSelection::minimum; } else if (x.test (">")) { m = HierarchyLevelSelection::maximum; } if (x.try_read (f)) { m_hier_levels.set_from_level (f, false, m); } } if (x.test ("..")) { int t; m = HierarchyLevelSelection::absolute; if (x.test ("*") || x.test ("(*)")) { m_hier_levels.set_to_level (std::numeric_limits::max (), false, HierarchyLevelSelection::absolute); } else if (x.test ("<*") || x.test ("(<*)")) { m_hier_levels.set_to_level (std::numeric_limits::max (), false, HierarchyLevelSelection::minimum); } else if (x.test ("(")) { if (x.test ("<")) { m = HierarchyLevelSelection::minimum; } else if (x.test (">")) { m = HierarchyLevelSelection::maximum; } x.read (t); x.expect(")"); m_hier_levels.set_to_level (t, true, m); } else { if (x.test ("<")) { m = HierarchyLevelSelection::minimum; } else if (x.test (">")) { m = HierarchyLevelSelection::maximum; } if (x.try_read (t)) { m_hier_levels.set_to_level (t, false, m); } } } else if (m_hier_levels.has_from_level ()) { // No explicit "to" spec: use "from" as "to" and put "0" into "from" place. bool fr = m_hier_levels.from_level_relative (); int f = m_hier_levels.from_level (); HierarchyLevelSelection::level_mode_type m = m_hier_levels.from_level_mode (); m_hier_levels.set_to_level (f, fr, m); m_hier_levels.set_from_level (0, fr, HierarchyLevelSelection::absolute); } } } else if (x.test ("/")) { if (x.test ("*")) { m_datatype = -1; } else { x.read (v); m_datatype = int (v); } } else if (x.test ("{")) { m_cell_sel.parse (x); x.expect ("}"); } else if (x.test ("*")) { m_layer = -1; } else if (x.try_read (v)) { m_layer = int (v); m_datatype = 0; } else if (x.test ("@")) { unsigned int ui = 0; if (x.test ("*")) { // .. @* -> cv_index=-1 } else { x.read (ui); } m_cv_index = int (ui) - 1; } else { x.skip (); if (*x == '\'' || *x == '"') { x.read_quoted (m_name); } else { x.read (m_name, "@([/*#%"); } m_has_name = true; } } if (m_trans.empty ()) { m_trans.push_back (db::DCplxTrans ()); } } db::LayerProperties ParsedLayerSource::layer_props () const { db::LayerProperties lp; if (has_name ()) { lp.name = name (); } if (m_layer >= 0) { lp.layer = m_layer; } if (m_datatype >= 0) { lp.datatype = m_datatype; } return lp; } unsigned int ParsedLayerSource::color_index () const { if (layer () >= 0) { // by default use the GDS layer number return (unsigned int) layer (); } else if (has_name ()) { // if there is no layer, use a simple hash value derived from the name. unsigned int ln = 0; const std::string &n = name (); for (const char *cp = n.c_str (); *cp; ++cp) { ln = ln * 37 + (unsigned int)*cp; } return ln; } else { // no specific ordering: no ordering return 0; } } bool ParsedLayerSource::is_wildcard_layer () const { return (m_special_purpose == SP_None && ! has_name () && m_layer < 0 && m_datatype < 0 && m_layer_index < 0); } bool ParsedLayerSource::match (const db::LayerProperties &lp) const { return layer_props ().log_equal (lp); } }