From 56f6143e4f1c29317a0e60d130977b2c1daa328d Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 10 May 2019 23:24:04 +0200 Subject: [PATCH 1/7] Added RBA::Technology#clear_technologies --- src/db/db/gsiDeclDbTechnologies.cc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/db/db/gsiDeclDbTechnologies.cc b/src/db/db/gsiDeclDbTechnologies.cc index a64378c85..061e1e433 100644 --- a/src/db/db/gsiDeclDbTechnologies.cc +++ b/src/db/db/gsiDeclDbTechnologies.cc @@ -70,6 +70,11 @@ static void technologies_from_xml (const std::string &s) db::Technologies::instance ()->load_from_xml (s); } +static void clear_technologies () +{ + db::Technologies::instance ()->clear (); +} + static db::Technology technology_from_xml (const std::string &s) { db::Technology tech; @@ -289,6 +294,11 @@ gsi::Class technology_decl ("db", "Technology", "\n" "\\technology_from_xml can be used to restore the technology definition." ) + + gsi::method ("clear_technologies", &clear_technologies, + "@brief Clears all technologies\n" + "\n" + "This method has been introduced in version 0.26.\n" + ) + gsi::method ("technologies_from_xml", &technologies_from_xml, gsi::arg ("xml"), "@brief Loads the technologies from a XML representation\n" "\n" From 252b1551dca2ac8fa5d7d879636e0f60a0a10b2e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Fri, 10 May 2019 23:43:57 +0200 Subject: [PATCH 2/7] Bugfix: strmrun issues - strmrun did not support x[rb] notation for file type - x.y.py was rejected because y.py was taken as the suffix - reason was extension() function for which there is an extension_last() now - but this function is no longer used as the lym::Macro object is used now --- src/buddies/src/bd/bd.pro | 6 +++--- src/buddies/src/bd/strmrun.cc | 14 ++++---------- src/tl/tl/tlFileUtils.cc | 10 ++++++++++ src/tl/tl/tlFileUtils.h | 5 +++++ src/tl/unit_tests/tlFileUtils.cc | 7 +++++++ 5 files changed, 29 insertions(+), 13 deletions(-) diff --git a/src/buddies/src/bd/bd.pro b/src/buddies/src/bd/bd.pro index 1b80e94f2..3ec14b734 100644 --- a/src/buddies/src/bd/bd.pro +++ b/src/buddies/src/bd/bd.pro @@ -31,9 +31,9 @@ HEADERS = \ RESOURCES = \ -INCLUDEPATH += $$TL_INC $$GSI_INC $$VERSION_INC $$DB_INC $$LIB_INC $$RDB_INC -DEPENDPATH += $$TL_INC $$GSI_INC $$VERSION_INC $$DB_INC $$LIB_INC $$RDB_INC -LIBS += -L$$DESTDIR -lklayout_tl -lklayout_db -lklayout_gsi -lklayout_lib -lklayout_rdb +INCLUDEPATH += $$TL_INC $$GSI_INC $$VERSION_INC $$DB_INC $$LIB_INC $$RDB_INC $$LYM_INC +DEPENDPATH += $$TL_INC $$GSI_INC $$VERSION_INC $$DB_INC $$LIB_INC $$RDB_INC $$LYM_INC +LIBS += -L$$DESTDIR -lklayout_tl -lklayout_db -lklayout_gsi -lklayout_lib -lklayout_rdb -lklayout_lym INCLUDEPATH += $$RBA_INC DEPENDPATH += $$RBA_INC diff --git a/src/buddies/src/bd/strmrun.cc b/src/buddies/src/bd/strmrun.cc index 9437162be..130c75c10 100644 --- a/src/buddies/src/bd/strmrun.cc +++ b/src/buddies/src/bd/strmrun.cc @@ -34,6 +34,7 @@ #include "gsiExpression.h" #include "libForceLink.h" #include "rdbForceLink.h" +#include "lymMacro.h" struct RunnerData { @@ -92,14 +93,7 @@ BD_PUBLIC int strmrun (int argc, char *argv[]) std::string script = tl::absolute_file_path (data.script); - std::string ext = tl::extension (data.script); - if (ext == "py") { - python.load_file (script); - } else if (ext == "rb") { - ruby.load_file (script); - } else { - throw tl::Exception (tl::to_string (tr ("Unknown suffix \"%s\" - must be either .rb or .py")), ext); - } - - return 0; + lym::Macro macro; + macro.load_from (script); + return macro.run (); } diff --git a/src/tl/tl/tlFileUtils.cc b/src/tl/tl/tlFileUtils.cc index 79adf0155..303b07b4f 100644 --- a/src/tl/tl/tlFileUtils.cc +++ b/src/tl/tl/tlFileUtils.cc @@ -308,6 +308,16 @@ std::string extension (const std::string &s) return tl::join (fnp, "."); } +std::string extension_last (const std::string &s) +{ + std::vector fnp = split_filename (filename (s)); + if (fnp.size () > 1) { + return fnp.back (); + } else { + return std::string (); + } +} + bool is_parent_path (const std::string &parent, const std::string &path) { diff --git a/src/tl/tl/tlFileUtils.h b/src/tl/tl/tlFileUtils.h index da2470591..766ffc819 100644 --- a/src/tl/tl/tlFileUtils.h +++ b/src/tl/tl/tlFileUtils.h @@ -90,6 +90,11 @@ std::string TL_PUBLIC basename (const std::string &s); */ std::string TL_PUBLIC extension (const std::string &s); +/** + * @brief Gets the last extension for a given file path + */ +std::string TL_PUBLIC extension_last (const std::string &s); + /** * @brief Returns true, if the given path exists * If the path is a directory, file_exists will return true, if the directory exists. diff --git a/src/tl/unit_tests/tlFileUtils.cc b/src/tl/unit_tests/tlFileUtils.cc index bf3f2ea9f..9b57c3176 100644 --- a/src/tl/unit_tests/tlFileUtils.cc +++ b/src/tl/unit_tests/tlFileUtils.cc @@ -391,6 +391,13 @@ TEST (10) EXPECT_EQ (tl::extension ("\\hello\\.world.gz"), "gz"); EXPECT_EQ (tl::extension ("/hello//world/"), ""); + EXPECT_EQ (tl::extension_last ("/hello/world"), ""); + EXPECT_EQ (tl::extension_last ("/hello/world.tar"), "tar"); + EXPECT_EQ (tl::extension_last ("/hello/world.tar.gz"), "gz"); + EXPECT_EQ (tl::extension_last ("\\hello\\.world"), ""); + EXPECT_EQ (tl::extension_last ("\\hello\\.world.gz"), "gz"); + EXPECT_EQ (tl::extension_last ("/hello//world/"), ""); + EXPECT_EQ (tl::is_absolute ("world"), false); EXPECT_EQ (tl::is_absolute ("world/"), false); EXPECT_EQ (tl::is_absolute ("hello//world/"), false); From 72cadf6d5dc54ec7c552be9f1d068931477b392f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 May 2019 02:29:38 +0200 Subject: [PATCH 3/7] WIP: more powerful glob pattern --- src/tl/tl/tlGlobPattern.cc | 735 +++++++++++++++++++++++-------- src/tl/tl/tlGlobPattern.h | 23 +- src/unit_tests/unit_test_main.cc | 4 +- 3 files changed, 581 insertions(+), 181 deletions(-) diff --git a/src/tl/tl/tlGlobPattern.cc b/src/tl/tl/tlGlobPattern.cc index 179c139fd..f9a98735f 100644 --- a/src/tl/tl/tlGlobPattern.cc +++ b/src/tl/tl/tlGlobPattern.cc @@ -22,234 +22,609 @@ #include "tlGlobPattern.h" +#include "tlString.h" + +#include namespace tl { -static bool -do_match (const char *p, const char *s, bool cs, bool exact, bool hm, std::vector *o, std::vector > &bstart) +// TODO: take from tlString.h +inline uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0) { - while (*p) { + uint32_t c32 = (unsigned char) *cp++; + if (c32 >= 0xf0 && ((cpe && cp + 2 < cpe) || (! cpe && cp [0] && cp [1] && cp [2]))) { + c32 = ((c32 & 0x7) << 18) | ((uint32_t (cp [0]) & 0x3f) << 12) | ((uint32_t (cp [1]) & 0x3f) << 6) | (uint32_t (cp [2]) & 0x3f); + cp += 3; + } else if (c32 >= 0xe0 && ((cpe && cp + 1 < cpe) || (! cpe && cp [0] && cp [1]))) { + c32 = ((c32 & 0xf) << 12) | ((uint32_t (cp [0]) & 0x3f) << 6) | (uint32_t (cp [1]) & 0x3f); + cp += 2; + } else if (c32 >= 0xc0 && ((cpe && cp < cpe) || (! cpe && cp [0]))) { + c32 = ((c32 & 0x1f) << 6) | (uint32_t (*cp) & 0x3f); + ++cp; + } - if (!exact && *p == '\\') { + return c32; +} - ++p; - if (!*s || *s != *p) { +class GlobPatternOp +{ +public: + GlobPatternOp () : m_next_owned (false), mp_next (0) { } + + virtual ~GlobPatternOp () + { + if (m_next_owned) { + delete mp_next; + } + mp_next = 0; + } + + virtual GlobPatternOp *clone () const + { + GlobPatternOp *op = new GlobPatternOp (); + if (next ()) { + op->set_next (next ()->clone ()); + } + return op; + } + + virtual bool match (const char *s, std::vector *e) const + { + size_t n = e ? e->size () : 0; + if (mp_next && mp_next->match (s, e)) { + return true; + } else if (! mp_next && ! *s) { + return true; + } else if (e) { + e->erase (e->begin () + n, e->end ()); + return false; + } else { + return false; + } + } + + void set_next (GlobPatternOp *next) + { + m_next_owned = true; + mp_next = next; + } + + GlobPatternOp *next () + { + return mp_next; + } + + const GlobPatternOp *next () const + { + return mp_next; + } + + void set_tail (GlobPatternOp *op) + { + GlobPatternOp *n = this; + while (n->mp_next) { + n = n->mp_next; + } + n->mp_next = op; + n->m_next_owned = false; + } + +private: + bool m_next_owned; + GlobPatternOp *mp_next; +}; + +class GlobPatternString + : public GlobPatternOp +{ +public: + GlobPatternString (const std::string &s, bool cs) + : GlobPatternOp (), m_s (s), m_cs (cs) + { + // .. nothing yet .. + } + + virtual GlobPatternOp *clone () const + { + GlobPatternString *op = new GlobPatternString (m_s, m_cs); + op->set_next (next ()->clone ()); + return op; + } + + virtual bool match (const char *s, std::vector *e) const + { + if (! m_cs && strncasecmp (s, m_s.c_str (), m_s.size ()) == 0) { + return GlobPatternOp::match (s + m_s.size (), e); + } else if (m_cs && strncmp (s, m_s.c_str (), m_s.size ()) == 0) { + return GlobPatternOp::match (s + m_s.size (), e); + } else { + return false; + } + } + +private: + std::string m_s; + bool m_cs; +}; + +class GlobPatternPass + : public GlobPatternOp +{ +public: + GlobPatternPass () + : GlobPatternOp () + { + // .. nothing yet .. + } + + virtual GlobPatternOp *clone () const + { + GlobPatternPass *op = new GlobPatternPass (); + if (next ()) { + op->set_next (next ()->clone ()); + } + return op; + } + + virtual bool match (const char *, std::vector *) const + { + return true; + } +}; + +class GlobPatternAny + : public GlobPatternOp +{ +public: + GlobPatternAny (size_t min, size_t max) + : GlobPatternOp (), m_min (min), m_max (max) + { + // .. nothing yet .. + } + + virtual GlobPatternOp *clone () const + { + GlobPatternAny *op = new GlobPatternAny (m_min, m_max); + if (next ()) { + op->set_next (next ()->clone ()); + } + return op; + } + + virtual bool match (const char *s, std::vector *e) const + { + size_t i = 0; + while (i <= m_max) { + if (i >= m_min && GlobPatternOp::match (s, e)) { + return true; + } else if (! *s) { return false; } - if (*p) { - ++p; - } - ++s; + utf32_from_utf8 (s); + ++i; + } - } else if (!exact && *p == '?') { + return false; + } - ++p; - if (! *s) { - return false; - } - ++s; +private: + size_t m_min, m_max; +}; - } else if (!exact && *p == '*') { +class GlobPatternBranch; - ++p; +template +class GlobPatternContinuator + : public GlobPatternOp +{ +public: + GlobPatternContinuator (T *br) + : mp_br (br) + { + // .. nothing yet .. + } - // a trailing '*' always matches - if (!*p) { + virtual GlobPatternOp *clone () const { return 0; } + + virtual bool match (const char *s, std::vector *e) const + { + return mp_br->continue_match (s, e); + } + +private: + T *mp_br; +}; + +class GlobPatternBranch + : public GlobPatternOp +{ +public: + GlobPatternBranch () + : GlobPatternOp (), m_cont (this) + { + // .. nothing yet .. + } + + ~GlobPatternBranch () + { + for (std::vector::const_iterator i = m_choices.begin (); i != m_choices.end (); ++i) { + delete *i; + } + m_choices.clear (); + } + + void add_choice (GlobPatternOp *op) + { + op->set_tail (&m_cont); + m_choices.push_back (op); + } + + virtual GlobPatternOp *clone () const + { + GlobPatternBranch *br = new GlobPatternBranch (); + if (next ()) { + br->set_next (next ()->clone ()); + } + for (std::vector::const_iterator i = m_choices.begin (); i != m_choices.end (); ++i) { + br->add_choice ((*i)->clone ()); + } + return br; + } + + virtual bool match (const char *s, std::vector *e) const + { + for (std::vector::const_iterator i = m_choices.begin (); i != m_choices.end (); ++i) { + if ((*i)->match (s, e)) { return true; } + } + return false; + } - std::vector > bs = bstart; - size_t no = o ? o->size () : 0; + virtual bool continue_match (const char *s, std::vector *e) const + { + return GlobPatternOp::match (s, e); + } - while (*s) { - if (do_match (p, s, cs, exact, hm, o, bstart)) { - return true; - } - bstart = bs; - if (o && o->begin () + no < o->end ()) { - o->erase (o->begin () + no, o->end ()); - } - ++s; - } +private: + std::vector m_choices; + GlobPatternContinuator m_cont; +}; - } else if (!exact && *p == '[') { +class GlobPatternBracket + : public GlobPatternOp +{ +public: + GlobPatternBracket () + : GlobPatternOp (), mp_inner (0), mp_s0 (0), m_cont (this) + { + // .. nothing yet .. + } - if (! *s) { - return false; - } + ~GlobPatternBracket () + { + delete mp_inner; + mp_inner = 0; + } - bool negate = false; - ++p; - if (*p && *p == '^') { - ++p; - negate = true; - } + void set_inner (GlobPatternOp *op) + { + delete mp_inner; + op->set_tail (& m_cont); + mp_inner = op; + } - bool hit = false; + virtual GlobPatternOp *clone () const + { + GlobPatternBracket *br = new GlobPatternBracket (); + if (next ()) { + br->set_next (next ()->clone ()); + } + if (mp_inner) { + br->set_inner (mp_inner->clone ()); + } + return br; + } - while (*p != ']' && *p) { + virtual bool match (const char *s, std::vector *e) const + { + if (mp_inner) { + mp_s0 = s; + bool res = mp_inner->match (s, e); + mp_s0 = 0; + return res; + } + return false; + } - char c1 = *p; - if (c1 == '\\') { - c1 = *++p; - } - if (*p) { - ++p; - } + virtual bool continue_match (const char *s, std::vector *e) const + { + if (mp_s0 && e) { + e->push_back (std::string (mp_s0, 0, s - mp_s0)); + } + return GlobPatternOp::match (s, e); + } - char c2 = c1; - if (*p == '-') { - ++p; - c2 = *p; - if (c2 == '\\') { - c2 = *++p; - } - if (*p) { - ++p; - } - } +private: + GlobPatternOp *mp_inner; + // NOTE: this isn't thread-safe unless GlobPattern objects live in different threads + mutable const char *mp_s0; + GlobPatternContinuator m_cont; +}; - if (! hit) { - if (cs && *s >= c1 && *s <= c2) { - hit = true; - // TODO: implement UTF-8 support - } else if (!cs && tolower (*s) >= tolower (c1) && tolower (*s) <= tolower (c2)) { - hit = true; - } - } +class GlobPatternCharClass + : public GlobPatternOp +{ +public: + GlobPatternCharClass (bool negate, bool cs) + : m_negate (negate), m_cs (cs) + { + // .. nothing yet .. + } - } + GlobPatternCharClass (const std::vector > &intervals, bool negate, bool cs) + : m_negate (negate), m_cs (cs), m_intervals (intervals) + { + // .. nothing yet .. + } - if (negate == hit) { - return false; - } + void add_interval (uint32_t c1, uint32_t c2) + { + m_intervals.push_back (std::make_pair (c1, c2)); + } - ++s; - if (*p) { - ++p; - } + virtual GlobPatternOp *clone () const + { + GlobPatternCharClass *op = new GlobPatternCharClass (m_intervals, m_negate, m_cs); + if (next ()) { + op->set_next (next ()->clone ()); + } + return op; + } - } else if (!exact && *p == '{') { - - ++p; - - bool hit = false; - const char *s0 = s; - - while (*p) { - - if (hit) { - - while (*p && *p != ',' && *p != '}') { - if (*p == '\\') { - ++p; - } - if (*p) { - ++p; - } - } + virtual bool match (const char *s, std::vector *e) const + { + uint32_t c = utf32_from_utf8 (s); + for (std::vector >::const_iterator i = m_intervals.begin (); i != m_intervals.end (); ++i) { + if (c >= i->first && c <= i->second) { + if (m_negate) { + return false; } else { - - s = s0; - hit = true; - while (*p && *p != ',' && *p != '}') { - if (*p == '\\') { - ++p; - } - if (hit) { - if (! *s) { - hit = false; - } else if (cs && *p != *s) { - hit = false; - // TODO: implement UTF-8 support - } else if (!cs && tolower (*p) != tolower (*s)) { - hit = false; - } else { - ++s; - } - } - if (*p) { - ++p; - } - } - + return GlobPatternOp::match (s, e); } + } + } - if (*p == ',') { - ++p; - } else if (*p == '}') { - ++p; - break; - } + if (! m_negate) { + return false; + } else { + return GlobPatternOp::match (s, e); + } + } +private: + bool m_negate, m_cs; + std::vector > m_intervals; +}; + +static +GlobPatternOp *compile (const char *&p, bool exact, bool cs, bool hm, bool for_brace); + +void +compile_emit_op (GlobPatternOp *&op_head, GlobPatternOp *&op, GlobPatternOp *no) +{ + if (op) { + op->set_next (no); + } else { + op_head = no; + } + op = no; +} + +void +compile_emit_string (std::string &str, GlobPatternOp *&op_head, GlobPatternOp *&op, bool cs) +{ + if (! str.empty ()) { + compile_emit_op (op_head, op, new GlobPatternString (str, cs)); + str.clear (); + } +} + +void +compile_emit_char_class (GlobPatternOp *&op_head, GlobPatternOp *&op, const char *&p, bool cs) +{ + bool negate = false; + if (*p && *p == '^') { + ++p; + negate = true; + } + + GlobPatternCharClass *cc = new GlobPatternCharClass (negate, cs); + + while (*p != ']' && *p) { + + uint32_t c1 = utf32_from_utf8 (p); + if (c1 == '\\') { + c1 = utf32_from_utf8 (p); + } + + uint32_t c2 = c1; + if (*p == '-') { + ++p; + c2 = utf32_from_utf8 (p); + if (c2 == '\\') { + c2 = utf32_from_utf8 (p); + } + } + + cc->add_interval (c1, c2); + + } + + compile_emit_op (op_head, op, cc); +} + +void +compile_emit_alt (GlobPatternOp *&op_head, GlobPatternOp *&op, const char *&p, bool cs) +{ + GlobPatternBranch *alt_op = new GlobPatternBranch (); + while (*p) { + GlobPatternOp *alt = compile (p, false, cs, false, true); + if (alt) { + alt_op->add_choice (alt); + } + if (*p == ',') { + ++p; + } else if (*p == '}') { + ++p; + break; + } + } + + compile_emit_op (op_head, op, alt_op); +} + +static +GlobPatternOp *compile (const char *&p, bool exact, bool cs, bool hm, bool for_brace) +{ + std::string str; + GlobPatternOp *op = 0, *op_head = 0; + + while (*p) { + + if (exact) { + + str += *++p; + + } else if (*p == '\\') { + + ++p; + if (*p) { + str += *++p; } - if (! hit) { - return false; - } + } else if (*p == '?') { - } else if (!exact && *p == ')') { + compile_emit_string (str, op_head, op, cs); + compile_emit_op (op_head, op, new GlobPatternAny (1, 1)); ++p; - if (! bstart.empty ()) { - if (o) { - (*o)[bstart.back ().first] = std::string (bstart.back ().second, s - bstart.back ().second); - } - bstart.pop_back (); - } + } else if (*p == '*') { - } else if (!exact && *p == '(') { + compile_emit_string (str, op_head, op, cs); + if (p[1]) { + compile_emit_op (op_head, op, new GlobPatternAny (0, std::numeric_limits::max ())); + } else { + compile_emit_op (op_head, op, new GlobPatternPass ()); + } ++p; - if (o) { - bstart.push_back (std::make_pair ((unsigned int) o->size (), s)); - o->push_back (std::string ()); - } + + } else if (*p == '[') { + + compile_emit_string (str, op_head, op, cs); + ++p; + compile_emit_char_class (op_head, op, p, cs); + + } else if (*p == '{') { + + compile_emit_string (str, op_head, op, cs); + ++p; + compile_emit_alt (op_head, op, p, cs); + + } else if (for_brace && (*p == ',' || *p == '}')) { + + break; } else { - if (cs) { - if (*s != *p) { - return false; - } else { - ++s; - ++p; - } - } else { - // TODO: implement UTF-8 support - if (tolower (*s) != tolower (*p)) { - return false; - } else { - ++s; - ++p; - } - } + str += *p++; } - } + } - return (hm || *s == 0); + compile_emit_string (str, op_head, op, cs); + + if (hm) { + compile_emit_op (op_head, op, new GlobPatternPass ()); + } + + return op_head; } GlobPattern::GlobPattern () : m_case_sensitive (true), m_exact (false), m_header_match (false) { - // .. nothing yet .. + mp_op = 0; + m_needs_compile = true; } GlobPattern::GlobPattern (const std::string &p) : m_p (p), m_case_sensitive (true), m_exact (false), m_header_match (false) { - // .. nothing yet .. + mp_op = 0; + m_needs_compile = true; +} + +GlobPattern::GlobPattern (const GlobPattern &other) + : m_case_sensitive (true), m_exact (false), m_header_match (false) +{ + mp_op = 0; + m_needs_compile = true; + + operator= (other); +} + +GlobPattern & +GlobPattern::operator= (const GlobPattern &other) +{ + if (this != &other) { + + m_case_sensitive = other.m_case_sensitive; + m_exact = other.m_exact; + m_header_match = other.m_header_match; + + m_needs_compile = true; + + } + return *this; +} + +void +GlobPattern::do_compile () +{ + delete mp_op; + + const char *p = m_p.c_str (); + mp_op = compile (p, m_exact, m_case_sensitive, m_header_match, false); + + if (! mp_op) { + mp_op = new GlobPatternOp (); + } + + m_needs_compile = false; +} + +GlobPattern &GlobPattern::operator= (const std::string &p) +{ + if (m_p != p) { + m_p = p; + m_needs_compile = true; + } + + return *this; } void GlobPattern::set_case_sensitive (bool f) { - m_case_sensitive = f; + if (f != m_case_sensitive) { + m_case_sensitive = f; + m_needs_compile = true; + } } bool GlobPattern::case_sensitive () const @@ -259,7 +634,10 @@ bool GlobPattern::case_sensitive () const void GlobPattern::set_exact (bool f) { - m_exact = f; + if (f != m_exact) { + m_exact = f; + m_needs_compile = true; + } } bool GlobPattern::exact () const @@ -269,7 +647,10 @@ bool GlobPattern::exact () const void GlobPattern::set_header_match (bool f) { - m_header_match = f; + if (f != m_header_match) { + m_header_match = f; + m_needs_compile = true; + } } bool GlobPattern::header_match () const @@ -277,10 +658,19 @@ bool GlobPattern::header_match () const return m_header_match; } +GlobPatternOp *GlobPattern::op () const +{ + if (m_needs_compile) { + GlobPattern *non_const_this = const_cast (this); + non_const_this->do_compile (); + } + + return mp_op; +} + bool GlobPattern::match (const char *s) const { - std::vector > bstart; - return do_match (m_p.c_str (), s, m_case_sensitive, m_exact, m_header_match, 0, bstart); + return op ()->match (s, 0); } bool GlobPattern::match (const char *s, std::vector &e) const @@ -288,14 +678,13 @@ bool GlobPattern::match (const char *s, std::vector &e) const if (! e.empty ()) { e.clear (); } - std::vector > bstart; - return do_match (m_p.c_str (), s, m_case_sensitive, m_exact, m_header_match, &e, bstart); + + return op ()->match (s, &e); } bool GlobPattern::match (const std::string &s) const { - std::vector > bstart; - return do_match (m_p.c_str (), s.c_str (), m_case_sensitive, m_exact, m_header_match, 0, bstart); + return op ()->match (s.c_str (), 0); } bool GlobPattern::match (const std::string &s, std::vector &e) const @@ -303,8 +692,8 @@ bool GlobPattern::match (const std::string &s, std::vector &e) cons if (! e.empty ()) { e.clear (); } - std::vector > bstart; - return do_match (m_p.c_str (), s.c_str (), m_case_sensitive, m_exact, m_header_match, &e, bstart); + + return op ()->match (s.c_str (), &e); } } diff --git a/src/tl/tl/tlGlobPattern.h b/src/tl/tl/tlGlobPattern.h index 7d4dcd430..f71ec4426 100644 --- a/src/tl/tl/tlGlobPattern.h +++ b/src/tl/tl/tlGlobPattern.h @@ -32,6 +32,8 @@ namespace tl { +class GlobPatternOp; + /** * @brief A class representing a glob pattern */ @@ -51,14 +53,20 @@ public: */ GlobPattern (const std::string &p); + /** + * @brief Copy constructor + */ + GlobPattern (const GlobPattern &other); + + /** + * @brief Assignment + */ + GlobPattern &operator= (const GlobPattern &other); + /** * @brief Assignment of a string */ - GlobPattern &operator= (const std::string &p) - { - m_p = p; - return *this; - } + GlobPattern &operator= (const std::string &s); /** * @brief Sets a value indicating whether to treat the match case sensitive @@ -124,9 +132,14 @@ public: private: std::string m_p; + GlobPatternOp *mp_op; bool m_case_sensitive; bool m_exact; bool m_header_match; + bool m_needs_compile; + + void do_compile (); + GlobPatternOp *op () const; }; } // namespace tl diff --git a/src/unit_tests/unit_test_main.cc b/src/unit_tests/unit_test_main.cc index 5225cdee4..8e16f6546 100644 --- a/src/unit_tests/unit_test_main.cc +++ b/src/unit_tests/unit_test_main.cc @@ -386,11 +386,9 @@ main_cont (int &argc, char **argv) std::vector inst_modules = tl::dir_entries (inst_dir, true, false); std::sort (inst_modules.begin (), inst_modules.end ()); - tl::GlobPattern pat ("*.ut"); - for (std::vector::const_iterator im = inst_modules.begin (); im != inst_modules.end (); ++im) { - if (! pat.match (*im)) { + if (tl::extension_last (*im) != "ut") { continue; } From b171ee5ae1e8929991c0d0cb3713881abdb2fa5f Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 May 2019 18:23:31 +0200 Subject: [PATCH 4/7] WIP: refactoring of glob pattern (goal is to support more pattern) --- src/tl/tl/tlGlobPattern.cc | 349 ++++++++++++++++++++++++++----------- src/tl/tl/tlGlobPattern.h | 1 + src/tl/tl/tlString.cc | 10 +- 3 files changed, 251 insertions(+), 109 deletions(-) diff --git a/src/tl/tl/tlGlobPattern.cc b/src/tl/tl/tlGlobPattern.cc index f9a98735f..8c8ffef01 100644 --- a/src/tl/tl/tlGlobPattern.cc +++ b/src/tl/tl/tlGlobPattern.cc @@ -29,8 +29,10 @@ namespace tl { +// --------------------------------------------------------------------------------- // TODO: take from tlString.h -inline uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0) + +static inline uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0) { uint32_t c32 = (unsigned char) *cp++; if (c32 >= 0xf0 && ((cpe && cp + 2 < cpe) || (! cpe && cp [0] && cp [1] && cp [2]))) { @@ -47,25 +49,62 @@ inline uint32_t utf32_from_utf8 (const char *&cp, const char *cpe = 0) return c32; } +#include "utf_casefolding.h" + +static inline wchar_t wdowncase (wchar_t c) +{ + int ch = c >> 8; + if (ch >= 0 && ch < int (sizeof (uc_tab) / sizeof (uc_tab[0])) && uc_tab[ch]) { + return uc_tab[ch][c & 0xff]; + } else { + return c; + } +} + +static inline uint32_t utf32_downcase (uint32_t c32) +{ + if (sizeof (wchar_t) == 2 && c32 >= 0x10000) { + return c32; + } else { + return uint32_t (wdowncase (wchar_t (c32))); + } +} + +// --------------------------------------------------------------------------------- + +class GlobPatternOpBase +{ +public: + GlobPatternOpBase () { } + virtual ~GlobPatternOpBase () { } + + virtual GlobPatternOpBase *clone () const = 0; + virtual bool match (const char *s, std::vector *e) const = 0; + + virtual GlobPatternOpBase *next () { return 0; } + virtual const GlobPatternOpBase *next () const { return 0; } + virtual void set_next (GlobPatternOpBase * /*next*/, bool /*owned*/) { tl_assert (false); } + +private: + GlobPatternOpBase (const GlobPatternOpBase &); + GlobPatternOpBase &operator= (const GlobPatternOpBase &); +}; + class GlobPatternOp + : public GlobPatternOpBase { public: GlobPatternOp () : m_next_owned (false), mp_next (0) { } virtual ~GlobPatternOp () { - if (m_next_owned) { - delete mp_next; - } - mp_next = 0; + set_next (0, false); } virtual GlobPatternOp *clone () const { GlobPatternOp *op = new GlobPatternOp (); - if (next ()) { - op->set_next (next ()->clone ()); - } + init_clone (op); return op; } @@ -84,35 +123,50 @@ public: } } - void set_next (GlobPatternOp *next) + virtual void set_next (GlobPatternOpBase *next, bool owned) { - m_next_owned = true; + if (mp_next && m_next_owned) { + delete mp_next; + } + + m_next_owned = owned; mp_next = next; } - GlobPatternOp *next () + GlobPatternOpBase *next () { return mp_next; } - const GlobPatternOp *next () const + const GlobPatternOpBase *next () const { return mp_next; } - void set_tail (GlobPatternOp *op) + void set_tail (GlobPatternOpBase *op) { - GlobPatternOp *n = this; - while (n->mp_next) { - n = n->mp_next; + GlobPatternOpBase *n = this; + while (n->next ()) { + n = n->next (); + } + + n->set_next (op, false); + } + +protected: + void init_clone (GlobPatternOp *op) const + { + if (mp_next && m_next_owned) { + op->set_next (mp_next->clone (), true); } - n->mp_next = op; - n->m_next_owned = false; } private: bool m_next_owned; - GlobPatternOp *mp_next; + GlobPatternOpBase *mp_next; + + GlobPatternOp (const GlobPatternOp &); + GlobPatternOp &operator= (const GlobPatternOp &); }; class GlobPatternString @@ -128,24 +182,45 @@ public: virtual GlobPatternOp *clone () const { GlobPatternString *op = new GlobPatternString (m_s, m_cs); - op->set_next (next ()->clone ()); + init_clone (op); return op; } virtual bool match (const char *s, std::vector *e) const { - if (! m_cs && strncasecmp (s, m_s.c_str (), m_s.size ()) == 0) { - return GlobPatternOp::match (s + m_s.size (), e); + if (! m_cs) { + + const char *sr = m_s.c_str (); + while (*sr) { + if (! *s) { + return false; + } + uint32_t cr = utf32_from_utf8 (sr); + uint32_t c = utf32_from_utf8 (s); + if (utf32_downcase (cr) != utf32_downcase (c)) { + return false; + } + } + + return GlobPatternOp::match (s, e); + } else if (m_cs && strncmp (s, m_s.c_str (), m_s.size ()) == 0) { + return GlobPatternOp::match (s + m_s.size (), e); + } else { + return false; + } } private: std::string m_s; bool m_cs; + + GlobPatternString (const GlobPatternString &); + GlobPatternString &operator= (const GlobPatternString &); }; class GlobPatternPass @@ -161,9 +236,7 @@ public: virtual GlobPatternOp *clone () const { GlobPatternPass *op = new GlobPatternPass (); - if (next ()) { - op->set_next (next ()->clone ()); - } + init_clone (op); return op; } @@ -171,6 +244,9 @@ public: { return true; } + + GlobPatternPass (const GlobPatternPass &); + GlobPatternPass &operator= (const GlobPatternPass &); }; class GlobPatternAny @@ -186,9 +262,7 @@ public: virtual GlobPatternOp *clone () const { GlobPatternAny *op = new GlobPatternAny (m_min, m_max); - if (next ()) { - op->set_next (next ()->clone ()); - } + init_clone (op); return op; } @@ -210,13 +284,78 @@ public: private: size_t m_min, m_max; + + GlobPatternAny (const GlobPatternAny &); + GlobPatternAny &operator= (const GlobPatternAny &); }; -class GlobPatternBranch; +class GlobPatternCharClass + : public GlobPatternOp +{ +public: + GlobPatternCharClass (bool negate, bool cs) + : m_negate (negate), m_cs (cs) + { + // .. nothing yet .. + } + + GlobPatternCharClass (const std::vector > &intervals, bool negate, bool cs) + : m_negate (negate), m_cs (cs), m_intervals (intervals) + { + // .. nothing yet .. + } + + void add_interval (uint32_t c1, uint32_t c2) + { + if (m_cs) { + m_intervals.push_back (std::make_pair (c1, c2)); + } else { + m_intervals.push_back (std::make_pair (utf32_downcase (c1), utf32_downcase (c2))); + } + } + + virtual GlobPatternOp *clone () const + { + GlobPatternCharClass *op = new GlobPatternCharClass (m_intervals, m_negate, m_cs); + init_clone (op); + return op; + } + + virtual bool match (const char *s, std::vector *e) const + { + uint32_t c = utf32_from_utf8 (s); + if (! m_cs) { + c = utf32_downcase (c); + } + + for (std::vector >::const_iterator i = m_intervals.begin (); i != m_intervals.end (); ++i) { + if (c >= i->first && c <= i->second) { + if (m_negate) { + return false; + } else { + return GlobPatternOp::match (s, e); + } + } + } + + if (! m_negate) { + return false; + } else { + return GlobPatternOp::match (s, e); + } + } + +private: + bool m_negate, m_cs; + std::vector > m_intervals; + + GlobPatternCharClass (const GlobPatternCharClass &); + GlobPatternCharClass &operator= (const GlobPatternCharClass &); +}; template class GlobPatternContinuator - : public GlobPatternOp + : public GlobPatternOpBase { public: GlobPatternContinuator (T *br) @@ -234,6 +373,9 @@ public: private: T *mp_br; + + GlobPatternContinuator (const GlobPatternContinuator &); + GlobPatternContinuator &operator= (const GlobPatternContinuator &); }; class GlobPatternBranch @@ -263,12 +405,10 @@ public: virtual GlobPatternOp *clone () const { GlobPatternBranch *br = new GlobPatternBranch (); - if (next ()) { - br->set_next (next ()->clone ()); - } for (std::vector::const_iterator i = m_choices.begin (); i != m_choices.end (); ++i) { br->add_choice ((*i)->clone ()); } + init_clone (br); return br; } @@ -290,6 +430,9 @@ public: private: std::vector m_choices; GlobPatternContinuator m_cont; + + GlobPatternBranch (const GlobPatternBranch &); + GlobPatternBranch &operator= (const GlobPatternBranch &); }; class GlobPatternBracket @@ -297,7 +440,7 @@ class GlobPatternBracket { public: GlobPatternBracket () - : GlobPatternOp (), mp_inner (0), mp_s0 (0), m_cont (this) + : GlobPatternOp (), mp_inner (0), mp_s0 (0), m_index (0), m_cont (this) { // .. nothing yet .. } @@ -318,22 +461,30 @@ public: virtual GlobPatternOp *clone () const { GlobPatternBracket *br = new GlobPatternBracket (); - if (next ()) { - br->set_next (next ()->clone ()); - } if (mp_inner) { br->set_inner (mp_inner->clone ()); } + init_clone (br); return br; } virtual bool match (const char *s, std::vector *e) const { if (mp_inner) { - mp_s0 = s; + + if (e) { + mp_s0 = s; + m_index = e->size (); + e->push_back (std::string ()); + } else { + mp_s0 = 0; + } + bool res = mp_inner->match (s, e); + mp_s0 = 0; return res; + } return false; } @@ -341,7 +492,7 @@ public: virtual bool continue_match (const char *s, std::vector *e) const { if (mp_s0 && e) { - e->push_back (std::string (mp_s0, 0, s - mp_s0)); + (*e) [m_index] = std::string (mp_s0, 0, s - mp_s0); } return GlobPatternOp::match (s, e); } @@ -350,63 +501,11 @@ private: GlobPatternOp *mp_inner; // NOTE: this isn't thread-safe unless GlobPattern objects live in different threads mutable const char *mp_s0; + mutable size_t m_index; GlobPatternContinuator m_cont; -}; -class GlobPatternCharClass - : public GlobPatternOp -{ -public: - GlobPatternCharClass (bool negate, bool cs) - : m_negate (negate), m_cs (cs) - { - // .. nothing yet .. - } - - GlobPatternCharClass (const std::vector > &intervals, bool negate, bool cs) - : m_negate (negate), m_cs (cs), m_intervals (intervals) - { - // .. nothing yet .. - } - - void add_interval (uint32_t c1, uint32_t c2) - { - m_intervals.push_back (std::make_pair (c1, c2)); - } - - virtual GlobPatternOp *clone () const - { - GlobPatternCharClass *op = new GlobPatternCharClass (m_intervals, m_negate, m_cs); - if (next ()) { - op->set_next (next ()->clone ()); - } - return op; - } - - virtual bool match (const char *s, std::vector *e) const - { - uint32_t c = utf32_from_utf8 (s); - - for (std::vector >::const_iterator i = m_intervals.begin (); i != m_intervals.end (); ++i) { - if (c >= i->first && c <= i->second) { - if (m_negate) { - return false; - } else { - return GlobPatternOp::match (s, e); - } - } - } - - if (! m_negate) { - return false; - } else { - return GlobPatternOp::match (s, e); - } - } - -private: - bool m_negate, m_cs; - std::vector > m_intervals; + GlobPatternBracket (const GlobPatternBracket &); + GlobPatternBracket &operator= (const GlobPatternBracket &); }; static @@ -416,7 +515,7 @@ void compile_emit_op (GlobPatternOp *&op_head, GlobPatternOp *&op, GlobPatternOp *no) { if (op) { - op->set_next (no); + op->set_next (no, true); } else { op_head = no; } @@ -443,7 +542,12 @@ compile_emit_char_class (GlobPatternOp *&op_head, GlobPatternOp *&op, const char GlobPatternCharClass *cc = new GlobPatternCharClass (negate, cs); - while (*p != ']' && *p) { + while (*p) { + + if (*p == ']') { + ++p; + break; + } uint32_t c1 = utf32_from_utf8 (p); if (c1 == '\\') { @@ -486,6 +590,21 @@ compile_emit_alt (GlobPatternOp *&op_head, GlobPatternOp *&op, const char *&p, b compile_emit_op (op_head, op, alt_op); } +void +compile_emit_bracket (GlobPatternOp *&op_head, GlobPatternOp *&op, const char *&p, bool cs) +{ + GlobPatternBracket *br_op = new GlobPatternBracket (); + GlobPatternOp *inner = compile (p, false, cs, false, true); + if (inner) { + br_op->set_inner (inner); + } + if (*p == ')') { + ++p; + } + + compile_emit_op (op_head, op, br_op); +} + static GlobPatternOp *compile (const char *&p, bool exact, bool cs, bool hm, bool for_brace) { @@ -496,13 +615,13 @@ GlobPatternOp *compile (const char *&p, bool exact, bool cs, bool hm, bool for_b if (exact) { - str += *++p; + str += *p++; } else if (*p == '\\') { ++p; if (*p) { - str += *++p; + str += *p++; } } else if (*p == '?') { @@ -535,7 +654,13 @@ GlobPatternOp *compile (const char *&p, bool exact, bool cs, bool hm, bool for_b ++p; compile_emit_alt (op_head, op, p, cs); - } else if (for_brace && (*p == ',' || *p == '}')) { + } else if (*p == '(') { + + compile_emit_string (str, op_head, op, cs); + ++p; + compile_emit_bracket (op_head, op, p, cs); + + } else if (for_brace && (*p == ',' || *p == '}' || *p == ')')) { break; @@ -587,8 +712,9 @@ GlobPattern::operator= (const GlobPattern &other) m_case_sensitive = other.m_case_sensitive; m_exact = other.m_exact; m_header_match = other.m_header_match; - - m_needs_compile = true; + m_p = other.m_p; + mp_op = other.mp_op ? other.mp_op->clone () : 0; + m_needs_compile = other.m_needs_compile; } return *this; @@ -609,11 +735,24 @@ GlobPattern::do_compile () m_needs_compile = false; } +void +GlobPattern::needs_compile () +{ + if (! m_needs_compile) { + + m_needs_compile = true; + + delete mp_op; + mp_op = 0; + + } +} + GlobPattern &GlobPattern::operator= (const std::string &p) { if (m_p != p) { m_p = p; - m_needs_compile = true; + needs_compile (); } return *this; @@ -623,7 +762,7 @@ void GlobPattern::set_case_sensitive (bool f) { if (f != m_case_sensitive) { m_case_sensitive = f; - m_needs_compile = true; + needs_compile (); } } @@ -636,7 +775,7 @@ void GlobPattern::set_exact (bool f) { if (f != m_exact) { m_exact = f; - m_needs_compile = true; + needs_compile (); } } @@ -649,7 +788,7 @@ void GlobPattern::set_header_match (bool f) { if (f != m_header_match) { m_header_match = f; - m_needs_compile = true; + needs_compile (); } } diff --git a/src/tl/tl/tlGlobPattern.h b/src/tl/tl/tlGlobPattern.h index f71ec4426..4b9d8ad44 100644 --- a/src/tl/tl/tlGlobPattern.h +++ b/src/tl/tl/tlGlobPattern.h @@ -139,6 +139,7 @@ private: bool m_needs_compile; void do_compile (); + void needs_compile (); GlobPatternOp *op () const; }; diff --git a/src/tl/tl/tlString.cc b/src/tl/tl/tlString.cc index 4c9285b99..1af15feec 100644 --- a/src/tl/tl/tlString.cc +++ b/src/tl/tl/tlString.cc @@ -46,7 +46,7 @@ static std::locale c_locale ("C"); #include "utf_casefolding.h" -wchar_t wdowncase (wchar_t c) +static inline wchar_t wdowncase (wchar_t c) { int ch = c >> 8; if (ch >= 0 && ch < int (sizeof (uc_tab) / sizeof (uc_tab[0])) && uc_tab[ch]) { @@ -56,7 +56,7 @@ wchar_t wdowncase (wchar_t c) } } -wchar_t wupcase (wchar_t c) +static inline wchar_t wupcase (wchar_t c) { int ch = c >> 8; if (ch >= 0 && ch < int (sizeof (lc_tab) / sizeof (lc_tab[0])) && lc_tab[ch]) { @@ -66,7 +66,7 @@ wchar_t wupcase (wchar_t c) } } -uint32_t utf32_downcase (uint32_t c32) +static inline uint32_t utf32_downcase (uint32_t c32) { if (sizeof (wchar_t) == 2 && c32 >= 0x10000) { return c32; @@ -75,7 +75,8 @@ uint32_t utf32_downcase (uint32_t c32) } } -uint32_t utf32_upcase (uint32_t c32) +/* Not used yet: +static inline uint32_t utf32_upcase (uint32_t c32) { if (sizeof (wchar_t) == 2 && c32 >= 0x10000) { return c32; @@ -83,6 +84,7 @@ uint32_t utf32_upcase (uint32_t c32) return uint32_t (wupcase (wchar_t (c32))); } } +*/ // ------------------------------------------------------------------------- // Conversion of UTF8 to wchar_t From dfb9cdad4fd63c08f58d83535923b4d9b5aa042e Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 May 2019 18:31:42 +0200 Subject: [PATCH 5/7] WIP: refactoring of glob pattern --- src/tl/unit_tests/tlGlobPattern.cc | 100 ++++++++++++++++++++++++++++- 1 file changed, 97 insertions(+), 3 deletions(-) diff --git a/src/tl/unit_tests/tlGlobPattern.cc b/src/tl/unit_tests/tlGlobPattern.cc index c39bca6d6..d73d5adf7 100644 --- a/src/tl/unit_tests/tlGlobPattern.cc +++ b/src/tl/unit_tests/tlGlobPattern.cc @@ -51,6 +51,16 @@ TEST(2) EXPECT_EQ (a.match ("bad"), true); EXPECT_EQ (a.match ("dba"), true); + // copy works too ... + tl::GlobPattern aa = a; + + EXPECT_EQ (aa.match ("abc"), true); + EXPECT_EQ (aa.match ("a"), true); + EXPECT_EQ (aa.match (""), false); + EXPECT_EQ (aa.match ("bcd"), false); + EXPECT_EQ (aa.match ("bad"), true); + EXPECT_EQ (aa.match ("dba"), true); + EXPECT_EQ (b.match ("abc"), false); EXPECT_EQ (b.match ("a"), false); EXPECT_EQ (b.match (""), false); @@ -72,6 +82,14 @@ TEST(3) EXPECT_EQ (a.match ("had"), true); EXPECT_EQ (a.match ("hax"), false); + // copy works too ... + tl::GlobPattern aa = a; + + EXPECT_EQ (aa.match ("ab"), true); + EXPECT_EQ (aa.match ("a"), false); + EXPECT_EQ (aa.match ("had"), true); + EXPECT_EQ (aa.match ("hax"), false); + tl::GlobPattern b ("a[0-9\\abcdef]"); EXPECT_EQ (b.match ("a0"), true); @@ -89,6 +107,14 @@ TEST(4) EXPECT_EQ (a.match ("a"), false); EXPECT_EQ (a.match ("had"), false); EXPECT_EQ (a.match ("hax"), true); + + // copy works too ... + tl::GlobPattern aa = a; + + EXPECT_EQ (aa.match ("ab"), false); + EXPECT_EQ (aa.match ("a"), false); + EXPECT_EQ (aa.match ("had"), false); + EXPECT_EQ (aa.match ("hax"), true); } TEST(5) @@ -102,6 +128,17 @@ TEST(5) EXPECT_EQ (a.match ("abx"), true); EXPECT_EQ (a.match ("ax"), false); EXPECT_EQ (a.match ("hadx"), true); + + // copy works too ... + tl::GlobPattern aa = a; + + EXPECT_EQ (aa.match ("ab"), true); + EXPECT_EQ (aa.match ("a"), false); + EXPECT_EQ (aa.match ("had"), true); + + EXPECT_EQ (aa.match ("abx"), true); + EXPECT_EQ (aa.match ("ax"), false); + EXPECT_EQ (aa.match ("hadx"), true); } TEST(6) @@ -114,9 +151,52 @@ TEST(6) EXPECT_EQ (a.match ("abch"), false); EXPECT_EQ (a.match ("adh"), false); EXPECT_EQ (a.match ("ah"), false); + + // copy works too ... + tl::GlobPattern aa = a; + + EXPECT_EQ (aa.match ("abcg"), true); + EXPECT_EQ (aa.match ("adg"), true); + EXPECT_EQ (aa.match ("ag"), false); + EXPECT_EQ (aa.match ("abch"), false); + EXPECT_EQ (aa.match ("adh"), false); + EXPECT_EQ (aa.match ("ah"), false); + } TEST(7) +{ + tl::GlobPattern a ("a{bc*,d?}g"); + + EXPECT_EQ (a.match ("abcg"), true); + EXPECT_EQ (a.match ("adg"), false); + EXPECT_EQ (a.match ("adxg"), true); + EXPECT_EQ (a.match ("adxyg"), false); + EXPECT_EQ (a.match ("ag"), false); + EXPECT_EQ (a.match ("abch"), false); + EXPECT_EQ (a.match ("abcg"), true); + EXPECT_EQ (a.match ("abchg"), true); + EXPECT_EQ (a.match ("abchhg"), true); + EXPECT_EQ (a.match ("adh"), false); + EXPECT_EQ (a.match ("ah"), false); + + // copy works too ... + tl::GlobPattern aa = a; + + EXPECT_EQ (aa.match ("abcg"), true); + EXPECT_EQ (aa.match ("adg"), false); + EXPECT_EQ (aa.match ("adxg"), true); + EXPECT_EQ (aa.match ("adxyg"), false); + EXPECT_EQ (aa.match ("ag"), false); + EXPECT_EQ (aa.match ("abch"), false); + EXPECT_EQ (aa.match ("abcg"), true); + EXPECT_EQ (aa.match ("abchg"), true); + EXPECT_EQ (aa.match ("abchhg"), true); + EXPECT_EQ (aa.match ("adh"), false); + EXPECT_EQ (aa.match ("ah"), false); +} + +TEST(8) { tl::GlobPattern a ("(*({bc,d}))(*)"); @@ -127,6 +207,13 @@ TEST(7) EXPECT_EQ (v[1], "bc"); EXPECT_EQ (v[2], "g"); + // copy works too ... + EXPECT_EQ (tl::GlobPattern (a).match ("abcg", v), true); + EXPECT_EQ (v.size (), size_t (3)); + EXPECT_EQ (v[0], "abc"); + EXPECT_EQ (v[1], "bc"); + EXPECT_EQ (v[2], "g"); + EXPECT_EQ (a.match ("bc", v), true); EXPECT_EQ (v.size (), size_t (3)); EXPECT_EQ (v[0], "bc"); @@ -134,7 +221,7 @@ TEST(7) EXPECT_EQ (v[2], ""); } -TEST(8) +TEST(9) { // case insensitive @@ -152,6 +239,13 @@ TEST(8) EXPECT_EQ (v[1], "Bc"); EXPECT_EQ (v[2], "G"); + // copy works too ... + EXPECT_EQ (tl::GlobPattern (a).match ("aBcG", v), true); + EXPECT_EQ (v.size (), size_t (3)); + EXPECT_EQ (v[0], "aBc"); + EXPECT_EQ (v[1], "Bc"); + EXPECT_EQ (v[2], "G"); + tl::GlobPattern b ("*a[bcd]"); EXPECT_EQ (b.match ("ab"), true); @@ -164,7 +258,7 @@ TEST(8) EXPECT_EQ (b.match ("aB"), true); } -TEST(9) +TEST(10) { // exact match @@ -182,7 +276,7 @@ TEST(9) EXPECT_EQ (a.match ("(*({bc,D}))(*)"), true); } -TEST(10) +TEST(11) { // header match From f72790e808fbb3d04a9d73351be29b71c5b0920b Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 May 2019 22:35:50 +0200 Subject: [PATCH 6/7] WIP: glob pattern - GSI binding to enable compatible implementations. --- src/gsi/gsi/gsiDeclTl.cc | 82 ++++++++++++++++++++++++++++++++++++++++ testdata/ruby/tlTest.rb | 42 ++++++++++++++++++++ 2 files changed, 124 insertions(+) diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index f9adbbb77..2840a21c7 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -26,6 +26,7 @@ #include "tlTimer.h" #include "tlProgress.h" #include "tlExpression.h" +#include "tlGlobPattern.h" // ---------------------------------------------------------------- // Logger binding @@ -557,6 +558,87 @@ Class decl_ExpressionWrapper ("tl", "Expression", "This class has been introduced in version 0.25.\n" ); + +static tl::GlobPattern *new_glob_pattern (const std::string &s) +{ + return new tl::GlobPattern (s); +} + +namespace { + +template +struct to_var_iterator + : public Iter +{ + typedef typename Iter::value_type original_value_type; + typedef typename tl::Variant *pointer; + typedef typename tl::Variant &reference; + typedef typename Iter::difference_type difference_type; + + to_var_iterator (const Iter &iter) + : Iter (iter) + { } + + pointer operator-> () + { + m_var = tl::Variant (Iter::operator* ()); + return &m_var; + } + + reference operator* () + { + m_var = tl::Variant (Iter::operator* ()); + return m_var; + } + +private: + tl::Variant m_var; +}; + +} + +static tl::Variant match (const tl::GlobPattern *pattern, const std::string &s) +{ + std::vector brackets; + if (pattern->match (s, brackets)) { + return tl::Variant (to_var_iterator::const_iterator> (brackets.begin ()), to_var_iterator::const_iterator> (brackets.end ())); + } else { + return tl::Variant (); + } +} + +Class decl_GlobPattern ("tl", "GlobPattern", + gsi::constructor ("new", &new_glob_pattern, gsi::arg ("pattern"), + "@brief Creates a new glob pattern match object\n" + ) + + gsi::method ("case_sensitive=", &tl::GlobPattern::set_case_sensitive, gsi::arg ("case_sensitive"), + "@brief Sets a value indicating whether the glob pattern match is case sensitive." + ) + + gsi::method ("case_sensitive", &tl::GlobPattern::case_sensitive, + "@brief Gets a value indicating whether the glob pattern match is case sensitive." + ) + + gsi::method ("head_match=", &tl::GlobPattern::set_header_match, gsi::arg ("head_match"), + "@brief Sets a value indicating whether trailing characters are allowed.\n" + "If this predicate is false, the glob pattern needs to match the full subject string. " + "If true, the match function will ignore trailing characters and return true if the " + "front part of the subject string matches." + ) + + gsi::method ("head_match", &tl::GlobPattern::header_match, + "@brief Gets a value indicating whether trailing characters are allowed.\n" + ) + + gsi::method_ext ("match", &match, gsi::arg ("subject"), + "@brief Matches the subject string against the pattern.\n" + "Returns nil if the subject string does not match the pattern. Otherwise returns a list " + "with the substrings captured in round brackets." + ), + "@brief A glob pattern matcher\n" + "This class is provided to make KLayout's glob pattern matching available to scripts too. " + "The intention is to provide an implementation which is compatible with KLayout's pattern " + "syntax.\n" + "\n" + "This class has been introduced in version 0.26." +); + } diff --git a/testdata/ruby/tlTest.rb b/testdata/ruby/tlTest.rb index f10ceeb59..3874931f8 100644 --- a/testdata/ruby/tlTest.rb +++ b/testdata/ruby/tlTest.rb @@ -215,6 +215,48 @@ class Tl_TestClass < TestBase end + # Glob pattern + def test_3_GlobPattern + + pat = RBA::GlobPattern::new("a*b") + + assert_equal(pat.case_sensitive, true) + assert_equal(pat.head_match, false) + + assert_equal(pat.match("ab") != nil, true) + assert_equal(pat.match("axb") != nil, true) + assert_equal(pat.match("Axb") != nil, false) + assert_equal(pat.match("abx") != nil, false) + assert_equal(pat.match("xab") != nil, false) + + pat.case_sensitive = false + assert_equal(pat.case_sensitive, false) + + assert_equal(pat.match("ab") != nil, true) + assert_equal(pat.match("axb") != nil, true) + assert_equal(pat.match("Axb") != nil, true) + assert_equal(pat.match("abx") != nil, false) + assert_equal(pat.match("xab") != nil, false) + + pat.head_match = true + assert_equal(pat.head_match, true) + + assert_equal(pat.match("ab") != nil, true) + assert_equal(pat.match("axb") != nil, true) + assert_equal(pat.match("Axb") != nil, true) + assert_equal(pat.match("abx") != nil, true) + assert_equal(pat.match("abx") == [], true) + assert_equal(pat.match("xab") != nil, false) + + pat = RBA::GlobPattern::new("(*)a(*)") + + assert_equal(pat.match("xb") != nil, false) + res = pat.match("xab") + assert_equal(res != nil, true) + assert_equal(res.join("/"), "x/b") + + end + end load("test_epilogue.rb") From 590f078d6af12c5788e95cbe0b0d5838804ce339 Mon Sep 17 00:00:00 2001 From: Matthias Koefferlein Date: Sat, 11 May 2019 22:37:24 +0200 Subject: [PATCH 7/7] WIP: cleanup. --- src/gsi/gsi/gsiDeclTl.cc | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/gsi/gsi/gsiDeclTl.cc b/src/gsi/gsi/gsiDeclTl.cc index 2840a21c7..3997bbd9f 100644 --- a/src/gsi/gsi/gsiDeclTl.cc +++ b/src/gsi/gsi/gsiDeclTl.cc @@ -640,5 +640,3 @@ Class decl_GlobPattern ("tl", "GlobPattern", ); } - -