source: tags/ms_r16q2/SL/REGEXPR/RegExpr.cxx

Last change on this file was 12783, checked in by westram, 10 years ago
  • publish TESTs for which nm 2.24 fails to export source location
File size: 5.4 KB
Line 
1// ============================================================= //
2//                                                               //
3//   File      : RegExpr.cxx                                     //
4//   Purpose   : Wrapper for ARBDB regular expressions           //
5//                                                               //
6//   Coded by Ralf Westram (coder@reallysoft.de) in April 2009   //
7//   Institute of Microbiology (Technical University Munich)     //
8//   http://www.arb-home.de/                                     //
9//                                                               //
10// ============================================================= //
11
12#include "RegExpr.hxx"
13#include <arb_match.h>
14#include <regex.h>
15
16using namespace std;
17
18struct GBS_regex { regex_t compiled; }; // definition exists twice (see ../../ARBDB/admatch.c)
19
20
21RegExpr::RegExpr(const std::string& expression_, bool ignore_case_)
22    : expression(expression_)
23    , ignore_case(ignore_case_)
24    , comreg(0)
25    , matches(0)
26{
27}
28
29RegExpr::~RegExpr() {
30    if (comreg) GBS_free_regexpr(comreg);
31    delete [] matches; 
32}
33
34void RegExpr::compile() const {
35    if (!comreg) {
36        delete [] matches; matches = NULL;
37
38        GB_ERROR error = 0;
39        comreg = GBS_compile_regexpr(expression.c_str(), ignore_case ? GB_IGNORE_CASE : GB_MIND_CASE, &error);
40        if (error) throw string(error);
41        re_assert(comreg);
42    }
43}
44
45void RegExpr::perform_match(const char *str, size_t offset) const {
46    /* Searches for first match (and submatches) in 'str'
47     *
48     * sets member 'matches' to array of match + subexpression matches (heap-copy)
49     * or to NULL if nothing matched
50     *
51     * If 'offset' > 0, then str is searched from position 'offset'.
52     * In this case it is assumed, that we are not at line start!
53     */
54
55    delete [] matches; matches = NULL;
56
57    size_t      subs      = subexpr_count();
58    regmatch_t *possMatch = (regmatch_t*)malloc((subs+1) * sizeof(regmatch_t));
59    int         eflags    = offset ? REG_NOTBOL : 0;
60    int         res       = regexec(&comreg->compiled, str+offset, subs+1, possMatch, eflags);
61
62    if (res != REG_NOMATCH) {
63        matches  = new RegMatch[subs+1];
64        for (size_t s = 0; s <= subs; s++) {
65            if (possMatch[s].rm_so != -1) { // real match
66                matches[s] = RegMatch(possMatch[s].rm_so+offset, possMatch[s].rm_eo+offset);
67            }
68        }
69        re_assert(matches[0].didMatch()); // complete match has to be found
70    }
71    free(possMatch);
72}
73
74const RegMatch *RegExpr::match(const std::string& versus, size_t offset) const {
75    if (!comreg) compile();                         // lazy compilation
76    perform_match(versus.c_str(), offset);
77    return (matches && matches[0].didMatch()) ? &matches[0] : NULL;
78}
79
80size_t RegExpr::subexpr_count() const {
81    if (!comreg) compile();                       // lazy compilation
82    return comreg->compiled.re_nsub;
83}
84
85const RegMatch *RegExpr::subexpr_match(size_t subnr) const {
86    // get subexpression match from last 'match()'
87    // (or NULL if subexpression 'subnr' did not match)
88    //
89    // 'subnr' is in range [1..subexpr_count()]
90
91    const RegMatch *result = 0;
92    if (matches) {
93        size_t subs = subexpr_count();
94        re_assert(subnr >= 1 && subnr <= subs); // illegal subexpression index
95        if (subnr >= 1 && subnr <= subs) {
96            if (matches[subnr].didMatch()) result = &matches[subnr];
97        }
98    }
99    return result;
100}
101
102// --------------------------------------------------------------------------------
103
104#ifdef UNIT_TESTS
105#ifndef TEST_UNIT_H
106#include <test_unit.h>
107#endif
108
109#define TEST_REGEX_MATCHES(str,regexpr,igCase,exp_match) do {           \
110        RegExpr exp(regexpr, igCase);                                   \
111        const RegMatch *match = exp.match(str);                         \
112        TEST_REJECT_NULL(match);                                     \
113        TEST_EXPECT_EQUAL(match->extract(str).c_str(), exp_match);      \
114    } while(0)
115
116#define TEST_REGEX_DOESNT_MATCH(str,regexpr,igCase) do {                \
117        RegExpr exp(regexpr, igCase);                                   \
118        const RegMatch *match = exp.match(str);                         \
119        TEST_EXPECT_NULL(match);                                        \
120    } while(0)
121
122#define TEST_REGEX_MATCHES_SUB1(str,regexpr,igCase,exp_match,exp_sub1match) do {        \
123        RegExpr exp(regexpr, igCase);                                                   \
124        const RegMatch *match = exp.match(str);                                         \
125        TEST_REJECT_NULL(match);                                                     \
126        TEST_EXPECT_EQUAL(match->extract(str).c_str(), exp_match);                      \
127        match = exp.subexpr_match(1);                                                   \
128        TEST_REJECT_NULL(match);                                                     \
129        TEST_EXPECT_EQUAL(match->extract(str).c_str(), exp_sub1match);                  \
130    } while(0)
131   
132void TEST_regexpr() {
133    TEST_REGEX_MATCHES("bla", "^bla$", false, "bla");
134
135    TEST_REGEX_DOESNT_MATCH("3;1406", "^bla$", true);
136    TEST_REGEX_MATCHES("3;1406", "^bla|", true, "");
137    TEST_REGEX_MATCHES_SUB1("3;1406", "^[0-9]+;([0-9]+)$", true, "3;1406", "1406");
138}
139TEST_PUBLISH(TEST_regexpr);
140
141#endif // UNIT_TESTS
142
143// --------------------------------------------------------------------------------
Note: See TracBrowser for help on using the repository browser.