source: icGREP/icgrep-devel/icgrep/UCD/PropertyObjects.cpp @ 5682

Last change on this file since 5682 was 5682, checked in by cameron, 20 months ago

String property regular expression support, including special cases for null and reflexive sets

File size: 14.2 KB
Line 
1/*
2 *  Copyright (c) 2017 International Characters, Inc.
3 *  This software is licensed to the public under the Open Software License 3.0.
4 *  icgrep is a trademark of International Characters, Inc.
5 *
6 */
7
8#include "PropertyObjects.h"
9#include "PropertyObjectTable.h"
10#include <llvm/Support/Casting.h>
11#include <algorithm>
12#include <assert.h>
13#include <sstream>
14#include <llvm/Support/raw_ostream.h>
15#include <llvm/Support/ErrorHandling.h>
16#include <toolchain/grep_pipeline.h>
17#include <util/aligned_allocator.h>
18#include <re/re_analysis.h>
19using namespace llvm;
20
21namespace UCD {
22
23std::string canonicalize_value_name(const std::string & prop_or_val) {
24    std::locale loc;
25    std::stringstream s;
26
27    for (char c : prop_or_val) {
28        if ((c != '_') && (c != ' ') && (c != '-')) {
29            s << std::tolower(c, loc);
30        }
31    }
32    return s.str();
33}
34
35const std::string & PropertyObject::GetPropertyValueGrepString() {
36    llvm::report_fatal_error("Property Value Grep String unsupported.");
37}
38
39const UnicodeSet PropertyObject::GetCodepointSet(const std::string &) {
40    llvm::report_fatal_error("Property " + UCD::property_full_name[the_property] + " unsupported.");
41}
42
43const UnicodeSet PropertyObject::GetCodepointSetMatchingPattern(re::RE * pattern) {
44    llvm::report_fatal_error("GetCodepointSetMatchingPattern unsupported");
45}
46   
47const UnicodeSet EnumeratedPropertyObject::GetCodepointSet(const std::string & value_spec) {
48    const int property_enum_val = GetPropertyValueEnumCode(value_spec);
49    if (property_enum_val < 0) {
50        llvm::report_fatal_error("Enumerated Property " + UCD::property_full_name[the_property] + ": unknown value: " + value_spec);
51    }
52    return GetCodepointSet(property_enum_val);
53}
54
55class PropertyValueAccumulator : public grep::MatchAccumulator {
56public:
57   
58    PropertyValueAccumulator(const char * searchBuffer, std::vector<std::string> & accumulatedPropertyValues)
59    : mSearchBuffer(searchBuffer), mParsedPropertyValueSet(accumulatedPropertyValues) {}
60   
61    void accumulate_match(const size_t lineNum, size_t line_start, size_t line_end) override;
62private:
63    const char * mSearchBuffer;
64    std::vector<std::string> & mParsedPropertyValueSet;
65};
66void PropertyValueAccumulator::accumulate_match(const size_t lineNum, size_t line_start, size_t line_end) {
67    assert (line_start <= line_end);
68    mParsedPropertyValueSet.emplace_back(mSearchBuffer + line_start, mSearchBuffer + line_end);
69}
70
71    const UnicodeSet EnumeratedPropertyObject::GetCodepointSetMatchingPattern(re::RE * pattern) {
72   
73   
74        AlignedAllocator<char, 32> alloc;
75        std::vector<std::string> accumulatedValues;
76       
77        const std::string & str = GetPropertyValueGrepString();
78       
79        const unsigned segmentSize = 8;
80        const auto n = str.length();
81        const auto w = 256 * segmentSize;
82        const auto m = w - (n % w);
83       
84        char * aligned = alloc.allocate(n + m, 0);
85        std::memcpy(aligned, str.data(), n);
86        std::memset(aligned + n, 0, m);
87       
88        PropertyValueAccumulator accum(aligned, accumulatedValues);
89        grepBuffer(pattern, aligned, n, & accum);
90        alloc.deallocate(aligned, 0);
91       
92        UnicodeSet a;
93        for (auto v : accumulatedValues) {
94            int e = GetPropertyValueEnumCode(v);
95            a = a + GetCodepointSet(e);
96        }
97        return a;
98}
99
100const UnicodeSet & EnumeratedPropertyObject::GetCodepointSet(const int property_enum_val) const {
101    assert (property_enum_val >= 0);
102    return *(property_value_sets[property_enum_val]);
103}
104
105std::vector<UnicodeSet> & EnumeratedPropertyObject::GetEnumerationBasisSets() {
106    // Return the previously computed vector of basis sets, if it exists.
107    if (LLVM_UNLIKELY(enumeration_basis_sets.empty())) {
108        // Otherwise compute and return.
109        // Basis set i is the set of all codepoints whose numerical enumeration code e
110        // has bit i set, i.e., (e >> i) & 1 == 1.
111        unsigned basis_count = 1;
112        while ((1UL << basis_count) < independent_enum_count) {
113            basis_count++;
114        }
115        for (unsigned i = 0; i < basis_count; i++) {
116            enumeration_basis_sets.push_back(UnicodeSet());
117            for (unsigned e = 0; e < independent_enum_count; e++) {
118                if (((e >> i) & 1UL) == 0) {
119                    enumeration_basis_sets[i] = enumeration_basis_sets[i] + *property_value_sets[e];
120                }
121            }
122        }
123    }
124    return enumeration_basis_sets;
125}
126
127const std::string & EnumeratedPropertyObject::GetPropertyValueGrepString() {
128    if (LLVM_LIKELY(mPropertyValueGrepString.empty())) {
129        std::stringstream buffer;
130        for (unsigned i = 0; i != property_value_full_names.size(); i++) {
131            buffer << property_value_full_names[i] + "\n";
132        }
133        for (unsigned i = 0; i != property_value_enum_names.size(); i++) {
134            if (property_value_enum_names[i] == property_value_full_names[i]) continue;
135            buffer << property_value_enum_names[i] + "\n";
136        }
137        for (auto & a : property_value_aliases) {
138            buffer << a.first + "\n";
139        }
140        mPropertyValueGrepString = buffer.str();
141    }
142    return mPropertyValueGrepString;
143}
144
145int EnumeratedPropertyObject::GetPropertyValueEnumCode(const std::string & value_spec) {
146    // The canonical full names are not stored in the precomputed alias map,
147    // to save space in the executable.   Add them if the property is used.
148    if (uninitialized) {
149        for (unsigned i = 0; i != property_value_full_names.size(); i++) {
150            property_value_aliases.insert({canonicalize_value_name(property_value_full_names[i]), i});
151        }
152        for (unsigned i = 0; i != property_value_enum_names.size(); i++) {
153            property_value_aliases.insert({canonicalize_value_name(property_value_enum_names[i]), i});
154        }
155        uninitialized = false;
156    }
157    const auto valit = property_value_aliases.find(canonicalize_value_name(value_spec));
158    if (valit == property_value_aliases.end())
159        return -1;
160    return valit->second;
161}
162
163PropertyObject::iterator ExtensionPropertyObject::begin() const {
164    if (const auto * obj = dyn_cast<EnumeratedPropertyObject>(property_object_table[base_property])) {
165        return obj->begin();
166    }
167    llvm::report_fatal_error("Iterators unsupported for this type of PropertyObject.");
168}
169
170PropertyObject::iterator ExtensionPropertyObject::end() const {
171    if (const auto * obj = dyn_cast<EnumeratedPropertyObject>(property_object_table[base_property])) {
172        return obj->end();
173    }
174    llvm::report_fatal_error("Iterators unsupported for this type of PropertyObject.");
175}
176
177const UnicodeSet ExtensionPropertyObject::GetCodepointSet(const std::string & value_spec) {
178    int property_enum_val = GetPropertyValueEnumCode(value_spec);
179    if (property_enum_val == -1) {
180        llvm::report_fatal_error("Extension Property " + UCD::property_full_name[the_property] +  ": unknown value: " + value_spec);
181    }
182    return GetCodepointSet(property_enum_val);
183}
184
185const UnicodeSet & ExtensionPropertyObject::GetCodepointSet(const int property_enum_val) const {
186    assert (property_enum_val >= 0);
187    return *(property_value_sets[property_enum_val]);
188}
189
190int ExtensionPropertyObject::GetPropertyValueEnumCode(const std::string & value_spec) {
191    return cast<EnumeratedPropertyObject>(property_object_table[base_property])->GetPropertyValueEnumCode(value_spec);
192}
193
194const std::string & ExtensionPropertyObject::GetPropertyValueGrepString() {
195    return property_object_table[base_property]->GetPropertyValueGrepString();
196}
197
198const UnicodeSet BinaryPropertyObject::GetCodepointSet(const std::string & value_spec) {
199    int property_enum_val = Binary_ns::Y;
200    if (value_spec.length() != 0) {
201        auto valit = Binary_ns::aliases_only_map.find(canonicalize_value_name(value_spec));
202        if (valit == Binary_ns::aliases_only_map.end()) {
203            llvm::report_fatal_error("Binary Property " + UCD::property_full_name[the_property] +  ": bad value: " + value_spec);
204        }
205        property_enum_val = valit->second;
206    }
207    return GetCodepointSet(property_enum_val);
208}
209
210const UnicodeSet & BinaryPropertyObject::GetCodepointSet(const int property_enum_val) {
211    if (property_enum_val == Binary_ns::Y) {
212        return mY;
213    }
214    if (mNoUninitialized) {
215        mN = ~mY;
216        mNoUninitialized = false;
217    }
218    return mN;
219}
220
221const UnicodeSet BinaryPropertyObject::GetCodepointSetMatchingPattern(re::RE * pattern) {
222    llvm::report_fatal_error("Enumerated Property GetCodepointSetMatchingPattern not yet implemented");
223}
224   
225const std::string & BinaryPropertyObject::GetPropertyValueGrepString() {
226    if (mPropertyValueGrepString.empty()) {
227        std::stringstream buffer;
228        for (const auto & prop : Binary_ns::aliases_only_map) {
229            buffer << std::get<0>(prop) + "\n";
230        }
231        mPropertyValueGrepString = buffer.str();
232    }
233    return mPropertyValueGrepString;
234}
235   
236const unsigned firstCodepointLengthAndVal(const std::string & s, codepoint_t & cp) {
237    size_t lgth = s.length();
238    if (lgth == 0) return 0;
239    unsigned char s0 = s[0];
240    cp = static_cast<codepoint_t>(s0);
241    if (s0 < 0x80) return 1;
242    if (lgth == 1) return 0;  // invalid UTF-8
243    cp = ((cp & 0x1F) << 6) | (s[1] & 0x3F);
244    if ((s0 >= 0xC2) && (s0 <= 0xDF)) return 2;
245    if (lgth == 2) return 0;  // invalid UTF-8
246    cp = ((cp & 0x3FFF) << 6) | (s[2] & 0x3F);
247    if ((s0 >= 0xE0) && (s0 <= 0xEF)) return 3;
248    if (lgth == 3) return 0;  // invalid UTF-8
249    cp = ((cp & 0x7FFF) << 6) | (s[3] & 0x3F);
250    if ((s0 >= 0xF0) && (s0 <= 0xF4)) return 4;
251    return 0;
252}
253   
254class SetByLineNumberAccumulator : public grep::MatchAccumulator {
255public:
256   
257    SetByLineNumberAccumulator(const std::vector<UCD::codepoint_t> & cps)
258    : mCodepointTableByLineNum(cps) {}
259   
260    void accumulate_match(const size_t lineNum, size_t line_start, size_t line_end) override;
261    UnicodeSet getAccumulatedSet() { return mAccumSet; }
262private:
263    const std::vector<UCD::codepoint_t> & mCodepointTableByLineNum;
264    UnicodeSet mAccumSet;
265};
266void SetByLineNumberAccumulator::accumulate_match(const size_t lineNum, size_t line_start, size_t line_end) {
267    assert (line_start <= line_end);
268    mAccumSet.insert(mCodepointTableByLineNum[lineNum]);
269}
270
271
272const UnicodeSet NumericPropertyObject::GetCodepointSet(const std::string & value_spec) {
273    if (value_spec == "NaN") return mNaNCodepointSet;
274    else {
275        UnicodeSet result_set;
276        unsigned val_bytes = value_spec.length();
277        const char * value_str = value_spec.c_str();
278        const char * search_str = mStringBuffer;
279        unsigned buffer_line = 0;
280        while (buffer_line < mExplicitCps.size()) {
281            const char * eol = strchr(search_str, '\n');
282            unsigned len = eol - search_str;
283            if ((len == val_bytes) && (memcmp(search_str, value_str, len) == 0)) {
284                result_set.insert(mExplicitCps[buffer_line]);
285            }
286            buffer_line++;
287            search_str = eol+1;
288        }
289        return result_set;
290    }
291}
292
293const UnicodeSet NumericPropertyObject::GetCodepointSetMatchingPattern(re::RE * pattern) {
294    UnicodeSet matched;
295    // TODO:  Should we allow matches to NaN???
296    SetByLineNumberAccumulator accum(mExplicitCps);
297    grepBuffer(pattern, mStringBuffer, mBufSize, & accum);
298    return matched + accum.getAccumulatedSet();
299}
300
301
302const UnicodeSet StringPropertyObject::GetCodepointSet(const std::string & value_spec) {
303    if (value_spec == "") return mNullCodepointSet;
304    else {
305        UnicodeSet result_set;
306        unsigned val_bytes = value_spec.length();
307        codepoint_t cp;
308        if (val_bytes == firstCodepointLengthAndVal(value_spec, cp)) {
309            if (mSelfCodepointSet.contains(cp)) {
310                result_set.insert(cp);
311            }
312        }
313        const char * value_str = value_spec.c_str();
314        const char * search_str = mStringBuffer;
315        unsigned buffer_line = 0;
316        while (buffer_line < mExplicitCps.size()) {
317            const char * eol = strchr(search_str, '\n');
318            unsigned len = eol - search_str;
319            if ((len == val_bytes) && (memcmp(search_str, value_str, len) == 0)) {
320                result_set.insert(mExplicitCps[buffer_line]);
321            }
322            buffer_line++;
323            search_str = eol+1;
324        }
325        return result_set;
326    }
327}
328
329const UnicodeSet StringPropertyObject::GetCodepointSetMatchingPattern(re::RE * pattern) {
330    UnicodeSet matched = *cast<UnicodeSet>(matchableCodepoints(pattern)) & mSelfCodepointSet;
331    if (re::matchesEmptyString(pattern)) {
332        matched = matched + mNullCodepointSet;
333    }
334    SetByLineNumberAccumulator accum(mExplicitCps);
335    grepBuffer(pattern, mStringBuffer, mBufSize, & accum);
336    return matched + accum.getAccumulatedSet();
337}
338   
339const UnicodeSet StringOverridePropertyObject::GetCodepointSet(const std::string & value_spec) {
340    // First step: get the codepoints from the base object and then remove any overridden ones.
341    UnicodeSet result_set = mBaseObject.GetCodepointSet(value_spec) - mOverriddenSet;
342    // Now search for additional entries.
343    unsigned val_bytes = value_spec.length();
344    const char * value_str = value_spec.c_str();
345    const char * search_str = mStringBuffer;
346    unsigned buffer_line = 0;
347    while (buffer_line < mExplicitCps.size()) {
348        const char * eol = strchr(search_str, '\n');
349        unsigned len = eol - search_str;
350        if ((len == val_bytes) && (memcmp(search_str, value_str, len) == 0)) {
351            result_set.insert(mExplicitCps[buffer_line]);
352        }
353        buffer_line++;
354        search_str = eol+1;
355    }
356    return result_set;
357}
358   
359   
360const UnicodeSet StringOverridePropertyObject::GetCodepointSetMatchingPattern(re::RE * pattern) {
361    UnicodeSet base_set = mBaseObject.GetCodepointSetMatchingPattern(pattern) - mOverriddenSet;
362    SetByLineNumberAccumulator accum(mExplicitCps);
363    grepBuffer(pattern, mStringBuffer, mBufSize, & accum);
364    return base_set + accum.getAccumulatedSet();
365}
366
367
368const std::string & ObsoletePropertyObject::GetPropertyValueGrepString() {
369    llvm::report_fatal_error("Property " + UCD::property_full_name[the_property] + " is obsolete.");
370}
371
372const UnicodeSet ObsoletePropertyObject::GetCodepointSet(const std::string &) {
373    llvm::report_fatal_error("Property " + UCD::property_full_name[the_property] + " is obsolete.");
374}
375
376
377}
Note: See TracBrowser for help on using the repository browser.