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

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