source: icGREP/icgrep-devel/icgrep/re/re_parser.cpp @ 4412

Last change on this file since 4412 was 4412, checked in by cameron, 5 years ago

Command line parameter -i for case-insensitive matching

File size: 29.7 KB
Line 
1/*
2 *  Copyright (c) 2015 International Characters.
3 *  This software is licensed to the public under the Open Software License 3.0.
4 *  icgrep is a trademark of International Characters.
5 */
6
7#include <re/re_parser.h>
8#include <re/re_name.h>
9#include <re/re_alt.h>
10#include <re/re_end.h>
11#include <re/re_rep.h>
12#include <re/re_seq.h>
13#include <re/re_start.h>
14#include <re/re_diff.h>
15#include <re/re_intersect.h>
16#include <re/re_assertion.h>
17#include <re/parsefailure.h>
18#include <UCD/CaseFolding_txt.h>
19#include <algorithm>
20
21
22// It would probably be best to enforce that {}, [], () must always
23// be balanced.   But legacy syntax allows } and ] to occur as literals
24// in certain contexts (no opening { or [, or immediately after [ or [^ ).
25// Perhaps this define should become a parameter.
26#define LEGACY_UNESCAPED_RBRAK_RBRACE_ALLOWED true
27#define LEGACY_UNESCAPED_HYPHEN_ALLOWED true
28
29
30namespace re {
31
32RE * RE_Parser::parse(const std::string & regular_expression, ModeFlagSet initialFlags) {
33    RE_Parser parser(regular_expression);
34    parser.fModeFlagSet = initialFlags;
35    RE * re = parser.parse_RE();
36    if (re == nullptr) {
37        throw ParseFailure("An unexpected parsing error occurred!");
38    }
39    return re;
40}
41
42inline RE_Parser::RE_Parser(const std::string & regular_expression)
43    : _cursor(regular_expression.begin())
44    , _end(regular_expression.end())
45    , fModeFlagSet(0)
46    {
47       
48    }
49   
50RE * makeLookAheadAssertion(RE * r) {
51    return makeAssertion(r, Assertion::Kind::Lookahead, Assertion::Sense::Positive);
52}
53
54RE * makeNegativeLookAheadAssertion(RE * r) {
55    return makeAssertion(r, Assertion::Kind::Lookahead, Assertion::Sense::Negative);
56}
57
58RE * makeLookBehindAssertion(RE * r) {
59    return makeAssertion(r, Assertion::Kind::Lookbehind, Assertion::Sense::Positive);
60}
61
62RE * makeNegativeLookBehindAssertion(RE * r) {
63    return makeAssertion(r, Assertion::Kind::Lookbehind, Assertion::Sense::Negative);
64}
65
66RE * makeAtomicGroup(RE * r) {
67    throw ParseFailure("Atomic grouping not supported.");
68}
69
70RE * makeBranchResetGroup(RE * r) {
71    // Branch reset groups only affect submatch numbering, but
72    // this has no effect in icgrep.
73    return r;
74}
75   
76RE * RE_Parser::parse_RE() {
77    RE * r = parse_alt();
78    if (_cursor != _end) { 
79        throw ParseFailure("Unrecognized junk remaining at end of regexp");
80    }
81    return r;
82}
83
84RE * RE_Parser::parse_alt() {
85    std::vector<RE *> alt;
86    for (;;) {
87        alt.push_back(parse_seq());
88        if (_cursor == _end || *_cursor != '|') {
89            break;
90        }
91        ++_cursor; // advance past the alternation character '|'
92    }
93    if (alt.empty())
94    {
95        throw NoRegularExpressionFound();
96    }
97    return makeAlt(alt.begin(), alt.end());
98}
99
100inline RE * RE_Parser::parse_seq() {
101    std::vector<RE *> seq;
102    for (;;) {
103        RE * re = parse_next_item();
104        if (re == nullptr) {
105            break;
106        }
107        seq.push_back(extend_item(re));
108    }
109    if (seq.empty())
110    {
111        throw NoRegularExpressionFound();
112    }
113    return makeSeq(seq.begin(), seq.end());
114}
115
116RE * RE_Parser::parse_next_item() {
117    RE * re = nullptr;
118    if (_cursor != _end) {
119        switch (*_cursor) {
120            case '(':
121                ++_cursor;
122                return parse_group();
123            case '^':
124                ++_cursor;
125                return makeStart();
126            case '$':
127                ++_cursor;
128                return makeEnd();
129            case '|': case ')':
130                return nullptr;  // This is ugly.
131            case '*': case '+': case '?': case '{':
132                throw NothingToRepeat();
133            case ']': case '}':
134                if (LEGACY_UNESCAPED_RBRAK_RBRACE_ALLOWED) {
135                    return build_CC(parse_utf8_codepoint());
136                }
137                else throw ParseFailure("Use  \\] or \\} for literal ] or }.");
138            case '[':
139                _cursor++;
140                return parse_charset();
141            case '.': // the 'any' metacharacter
142                _cursor++;
143                return makeAny();
144            case '\\'// escape processing
145                ++_cursor;
146                return parse_escaped();
147            default:
148                return build_CC(parse_utf8_codepoint());
149        }
150    }
151    return re;
152}
153   
154   
155// Parse some kind of parenthesized group.  Input precondition: _cursor
156// after the (
157RE * RE_Parser::parse_group() {
158    RE * subexpr;
159    RE * group_expr;
160    ModeFlagSet savedModeFlags = fModeFlagSet;
161    throw_incomplete_expression_error_if_end_of_stream();
162    if (*_cursor == '?') {
163        ++_cursor;
164        throw_incomplete_expression_error_if_end_of_stream();
165        switch (*_cursor) {
166            case '#'// comment
167                ++_cursor;
168                while (_cursor != _end && *_cursor != ')') {
169                    ++_cursor;
170                }
171                throw_incomplete_expression_error_if_end_of_stream();
172                ++_cursor;
173                return parse_next_item();
174            case ':':  // Non-capturing paren
175                ++_cursor;
176                group_expr = parse_alt();
177                break;
178            case '=':
179                ++_cursor;
180                subexpr = parse_alt();
181                group_expr = makeLookAheadAssertion(subexpr);
182                break;
183            case '!':
184                ++_cursor;
185                subexpr = parse_alt();
186                group_expr = makeNegativeLookAheadAssertion(subexpr);
187                break;
188            case '>':
189                ++_cursor;
190                subexpr = parse_alt();
191                group_expr = makeAtomicGroup(subexpr);
192                break;
193            case '|':
194                ++_cursor;
195                subexpr = parse_alt();
196                group_expr = makeBranchResetGroup(subexpr);
197                break;
198            case '<':
199                ++_cursor;
200                throw_incomplete_expression_error_if_end_of_stream();
201                if (*_cursor == '=') {
202                    ++_cursor;
203                    subexpr = parse_alt();
204                    group_expr = makeLookBehindAssertion(subexpr);
205                }
206                else if (*_cursor == '!') {
207                    ++_cursor;
208                    subexpr = parse_alt();
209                    group_expr = makeNegativeLookBehindAssertion(subexpr);
210                }
211                else {
212                    throw ParseFailure("Illegal lookbehind assertion syntax.");
213                }
214                break;
215            case '-': case 'd' : case 'i': case 'm': case 's': case 'x': {
216                bool negateMode = false;
217                ModeFlagType modeBit;
218                while (_cursor != _end && *_cursor != ')' && *_cursor != ':') {
219                    if (*_cursor == '-') {
220                        negateMode = true;
221                        _cursor++;
222                        throw_incomplete_expression_error_if_end_of_stream();
223                    }
224                    switch (*_cursor++) {
225                        case 'i': modeBit = CASE_INSENSITIVE_MODE_FLAG; break;
226                        case 'm': modeBit = MULTILINE_MODE_FLAG; break;
227                        case 's': modeBit = DOTALL_MODE_FLAG; break;
228                        case 'x': modeBit = IGNORE_SPACE_MODE_FLAG; break;
229                        case 'd': modeBit = UNIX_LINES_MODE_FLAG; break;
230                        default: throw ParseFailure("Unrecognized mode flag.");
231                    }
232                    if (negateMode) {
233                        fModeFlagSet &= ~modeBit;
234                        negateMode = false;  // for next flag
235                    }
236                    else fModeFlagSet |= modeBit;
237                }
238                throw_incomplete_expression_error_if_end_of_stream();
239                if (*_cursor == ':') {
240                    ++_cursor;
241                    group_expr = parse_alt();
242                }
243                else {  // if *_cursor == ')'
244                    ++_cursor;
245                    // return immediately without restoring mode flags
246                    return parse_next_item();
247                }
248                break;
249            }
250            default:
251                throw ParseFailure("Illegal (? syntax.");
252        }
253    }
254    else {
255        // Capturing paren group, but ignore capture in icgrep.
256        group_expr = parse_alt();
257    }
258    // Restore mode flags.
259    fModeFlagSet = savedModeFlags;
260    if (_cursor == _end || *_cursor++ != ')')
261        throw ParseFailure("Closing paren required.");
262    return group_expr;
263}
264   
265RE * RE_Parser::extend_item(RE * re) {
266    int lower_bound, upper_bound;
267    if (_cursor == _end) {
268        return re;
269    }
270    switch (*_cursor) {
271        case '*':
272            lower_bound = 0;
273            upper_bound = Rep::UNBOUNDED_REP;
274            break;
275        case '?':
276            lower_bound = 0;
277            upper_bound = 1;
278            break;
279        case '+':
280            lower_bound = 1;
281            upper_bound = Rep::UNBOUNDED_REP;
282            break;
283        case '{':
284            parse_range_bound(lower_bound, upper_bound);
285            break;
286        default:
287            return re;
288    }
289    ++_cursor;
290    if (*_cursor == '?') {
291        // Non-greedy qualifier
292        ++_cursor;
293        //return makeNonGreedyRep(re, lower_bound, upper_bound);
294        // Greedy vs. non-greedy makes no difference for icgrep.
295        return makeRep(re, lower_bound, upper_bound);
296    }
297    else if (*_cursor == '+') {
298        // Possessive qualifier
299        ++_cursor;
300        //return makePossessiveRep(re, lower_bound, upper_bound);
301        throw ParseFailure("Possessive repetition is not supported in icgrep 1.0");
302    }
303    else {
304        // Normal repetition operator.
305        return makeRep(re, lower_bound, upper_bound);
306    }
307}
308
309inline void RE_Parser::parse_range_bound(int & lower_bound, int & upper_bound) {
310    ++_cursor;
311    throw_incomplete_expression_error_if_end_of_stream();
312    if (*_cursor == ',') {
313        ++_cursor;
314        lower_bound = 0;
315    }
316    else {
317        lower_bound = parse_int();
318    }
319    throw_incomplete_expression_error_if_end_of_stream();
320    if (*_cursor == '}') {
321        upper_bound = lower_bound;
322    }
323    else if (*_cursor != ',') {
324        throw BadLowerBound();
325    }
326    else { // [^,}]
327        ++_cursor;
328        throw_incomplete_expression_error_if_end_of_stream();
329        if (*_cursor == '}') {
330            upper_bound = Rep::UNBOUNDED_REP;
331        }
332        else {
333            upper_bound = parse_int();
334            if (*_cursor != '}') {
335                throw BadUpperBound();
336            }
337        }
338    }
339}
340
341unsigned RE_Parser::parse_int() {
342    unsigned value = 0;
343    for (; _cursor != _end; ++_cursor) {
344        if (!isdigit(*_cursor)) {
345            break;
346        }
347        value *= 10;
348        value += static_cast<int>(*_cursor) - 48;
349    }
350    return value;
351}
352
353
354#define bit40(x) (1ULL << ((x) - 0x40))
355const uint64_t setEscapeCharacters = bit40('b') | bit40('p') | bit40('d') | bit40('w') | bit40('s') | 
356                                     bit40('B') | bit40('P') | bit40('D') | bit40('W') | bit40('S');
357
358inline bool isSetEscapeChar(char c) {
359    return c >= 0x40 && c <= 0x7F && ((setEscapeCharacters >> (c - 0x40)) & 1) == 1;
360}
361
362inline RE * RE_Parser::parse_escaped() {
363    throw_incomplete_expression_error_if_end_of_stream();
364    if (isSetEscapeChar(*_cursor)) 
365      return parse_escaped_set();
366    else 
367      return build_CC(parse_escaped_codepoint());
368}
369
370RE * makeDigitSet() {
371    return makeName("Nd", Name::Type::UnicodeProperty);
372}
373
374RE * makeWhitespaceSet() {
375    return makeName("Whitespace", Name::Type::UnicodeProperty);
376}
377
378RE * makeWordSet() {
379    return makeName("word", Name::Type::UnicodeProperty);
380}
381
382RE * makeComplement(RE * s) {
383  return makeDiff(makeAny(), s);
384}
385
386RE * makeWordBoundary () {
387    RE * wordC = makeWordSet();
388    return makeAlt({makeSeq({makeNegativeLookBehindAssertion(wordC), makeLookAheadAssertion(wordC)}),
389                    makeSeq({makeLookBehindAssertion(wordC), makeNegativeLookAheadAssertion(wordC)})});
390}
391
392RE * makeWordNonBoundary () {
393    RE * wordC = makeWordSet();
394    return makeAlt({makeSeq({makeNegativeLookBehindAssertion(wordC), makeNegativeLookAheadAssertion(wordC)}),
395                    makeSeq({makeLookBehindAssertion(wordC), makeLookAheadAssertion(wordC)})});
396}
397
398RE * RE_Parser::parse_escaped_set() {
399    bool complemented = false;
400    RE * s;
401    switch (*_cursor) {
402        case 'b':
403            ++_cursor;
404            return makeWordBoundary();
405        case 'B':
406            ++_cursor;
407            return makeWordNonBoundary();
408        case 'd':
409            ++_cursor;
410            return makeDigitSet();
411        case 'D':
412            ++_cursor;
413            return makeComplement(makeDigitSet());
414        case 's':
415            ++_cursor;
416            return makeWhitespaceSet();
417        case 'S':
418            ++_cursor;
419            return makeComplement(makeWhitespaceSet());
420        case 'w':
421            ++_cursor;
422            return makeWordSet();
423        case 'W':
424            ++_cursor;
425            return makeComplement(makeWordSet());
426        case 'P':
427            complemented = true;
428        case 'p':
429            ++_cursor;
430            if (_cursor == _end || *_cursor != '{') throw ParseFailure("Malformed property expression");
431            ++_cursor;
432            s = parse_property_expression();
433            if (_cursor == _end || *_cursor != '}') throw ParseFailure("Malformed property expression");
434            ++_cursor;
435            if (complemented) return makeComplement(s);
436            else return s;
437        default:
438            throw ParseFailure("Internal error");
439    }
440}
441
442codepoint_t RE_Parser::parse_utf8_codepoint() {
443    // Must cast to unsigned char to avoid sign extension.
444    unsigned char pfx = static_cast<unsigned char>(*_cursor++);
445    codepoint_t cp = pfx;
446    if (pfx < 0x80) return cp;
447    unsigned suffix_bytes;
448    if (pfx < 0xE0) {
449        if (pfx < 0xC2) {  // bare suffix or illegal prefix 0xC0 or 0xC2
450            throw InvalidUTF8Encoding();
451        }
452        suffix_bytes = 1;
453        cp &= 0x1F;
454    }
455    else if (pfx < 0xF0) { // [0xE0, 0xEF]
456        cp &= 0x0F;
457        suffix_bytes = 2;
458    }
459    else { // [0xF0, 0xFF]
460        cp &= 0x0F;
461        suffix_bytes = 3;
462    }
463    while (suffix_bytes--) {
464        if (_cursor == _end) {
465            throw InvalidUTF8Encoding();
466        }
467        unsigned char sfx = static_cast<unsigned char>(*_cursor++);
468        if ((sfx & 0xC0) != 0x80) {
469            throw InvalidUTF8Encoding();
470        }
471        cp = (cp << 6) | (sfx & 0x3F);
472    }
473    // It is an error if a 3-byte sequence is used to encode a codepoint < 0x800
474    // or a 4-byte sequence is used to encode a codepoint < 0x10000.
475    if ((pfx == 0xE0 && cp < 0x800) || (pfx == 0xF0 && cp < 0x10000)) {
476        throw InvalidUTF8Encoding();
477    }
478    // It is an error if a 4-byte sequence is used to encode a codepoint
479    // above the Unicode maximum.   
480    if (cp > CC::UNICODE_MAX) {
481        throw InvalidUTF8Encoding();
482    }
483    return cp;
484}
485
486Name * RE_Parser::parse_property_expression() {
487    const cursor_t start = _cursor;
488    while (_cursor != _end && *_cursor != '}' and *_cursor != ':' and *_cursor != '=') {
489        _cursor++;
490    }
491    if (_cursor != _end && *_cursor == '=') {
492        const cursor_t prop_end = _cursor;
493        _cursor++;
494        const cursor_t val_start = _cursor;
495        while (_cursor != _end && *_cursor != '}' and *_cursor != ':') {
496            _cursor++;
497        }
498        // We have a property-name = value expression
499        return makeName(std::string(start, prop_end), std::string(val_start, _cursor), Name::Type::UnicodeProperty);
500    }
501    else return makeName(std::string(start, _cursor), Name::Type::UnicodeProperty);
502}
503
504CharsetOperatorKind RE_Parser::getCharsetOperator() {
505    throw_incomplete_expression_error_if_end_of_stream();
506    switch (*_cursor) {
507        case '&':
508            ++_cursor;
509            if (_cursor != _end && *_cursor == '&') {
510                ++_cursor;
511                return intersectOp;
512            }
513            else if (_cursor != _end && *_cursor == '[') {
514                // Short-hand for intersectOp when a set follows
515                return intersectOp;
516            }
517            else return ampChar;
518        case '-':
519            ++_cursor;
520            if (_cursor != _end && *_cursor == '-') {
521                ++_cursor;
522                return setDiffOp;
523            }
524            else if (_cursor != _end && *_cursor == '[') {
525                return setDiffOp;
526            }
527            else if (_cursor != _end && *_cursor == ']') {
528                return hyphenChar;
529            }
530            else return rangeHyphen;
531        case '[':
532            ++_cursor;
533            if (_cursor != _end && *_cursor == ':') {
534                ++_cursor;
535                return posixPropertyOpener;
536            }
537            else return setOpener;
538        case ']':
539            ++_cursor;
540            return setCloser;
541        case '\\':
542            ++_cursor;
543            return backSlash;
544        default:
545            return emptyOperator;
546    }
547}           
548   
549// Precondition: cursor is immediately after opening '[' character
550RE * RE_Parser::parse_charset() {
551    // Sets are accumulated using two variables:
552    // subexprs accumulates set expressions such as \p{Lu}, [\w && \p{Greek}]
553    // cc accumulates the literal and calculated codepoints and ranges
554    std::vector<RE *> subexprs;
555    CC * cc = makeCC();
556    // When the last item deal with is a single literal charcacter or calculated codepoint,
557    // a following hyphen can indicate a range.   When the last item is a set subexpression,
558    // a following hyphen can indicate set subtraction.
559    enum {NoItem, CodepointItem, RangeItem, SetItem, BrackettedSetItem} lastItemKind = NoItem;
560    codepoint_t lastCodepointItem;
561   
562    bool havePendingOperation = false;
563    CharsetOperatorKind pendingOperationKind;
564    RE * pendingOperand;
565   
566    // If the first character after the [ is a ^ (caret) then the matching character class is complemented.
567    bool negated = false;
568    if (_cursor != _end && *_cursor == '^') {
569      negated = true;
570      ++_cursor;
571    }
572    throw_incomplete_expression_error_if_end_of_stream();
573    // Legacy rule: an unescaped ] may appear as a literal set character
574    // if and only if it appears immediately after the opening [ or [^
575    if ( *_cursor == ']' && LEGACY_UNESCAPED_RBRAK_RBRACE_ALLOWED) {
576        cc->insert(']');
577        lastItemKind = CodepointItem;
578        lastCodepointItem = static_cast<codepoint_t> (']');
579        ++_cursor;
580    }
581    else if ( *_cursor == '-' && LEGACY_UNESCAPED_HYPHEN_ALLOWED) {
582        ++_cursor;
583        cc->insert('-');
584        lastItemKind = CodepointItem;
585        lastCodepointItem = static_cast<codepoint_t> ('-');
586                if (*_cursor == '-') throw ParseFailure("Set operator has no left operand.");
587    }
588    while (_cursor != _end) {
589        CharsetOperatorKind op = getCharsetOperator();
590        switch (op) {
591            case intersectOp: case setDiffOp: {
592                if (lastItemKind == NoItem) throw ParseFailure("Set operator has no left operand.");
593                if (cc->begin() != cc->end()) subexprs.push_back(cc);
594                RE * newOperand = makeAlt(subexprs.begin(), subexprs.end());
595                subexprs.clear();
596                cc = makeCC();
597                if (havePendingOperation) {
598                    if (pendingOperationKind == intersectOp) {
599                        pendingOperand = makeIntersect(pendingOperand, newOperand);
600                    }
601                    else {
602                        pendingOperand = makeDiff(pendingOperand, newOperand);
603                    }
604                }
605                else {
606                    pendingOperand = newOperand;
607                }
608                havePendingOperation = true;
609                pendingOperationKind = op;
610                lastItemKind = NoItem;
611            }
612            break;
613            case setCloser: {
614                if (lastItemKind == NoItem) throw ParseFailure("Set operator has no right operand.");
615                if (cc->begin() != cc->end()) {
616                    subexprs.push_back(cc);
617                }
618                RE * newOperand = makeAlt(subexprs.begin(), subexprs.end());
619                if (havePendingOperation) {
620                    if (pendingOperationKind == intersectOp) {
621                        newOperand = makeIntersect(pendingOperand, newOperand);
622                    }
623                    else {
624                        newOperand = makeDiff(pendingOperand, newOperand);
625                    }
626                }
627                if (fModeFlagSet & CASE_INSENSITIVE_MODE_FLAG) {
628                    if (CC * cc1 = dyn_cast<CC>(newOperand)) {
629                        newOperand = caseInsensitize(cc1);
630                    }
631                }
632                if (negated) return makeComplement(newOperand); 
633                else return newOperand;
634            }
635            case setOpener: case posixPropertyOpener: {
636                if (lastItemKind != NoItem) {
637                    if (cc->begin() != cc->end()) subexprs.push_back(cc);
638                    RE * newOperand = makeAlt(subexprs.begin(), subexprs.end());
639                    subexprs.clear();
640                    cc = makeCC();
641                    if (havePendingOperation) {
642                        if (pendingOperationKind == intersectOp) {
643                            pendingOperand = makeIntersect(pendingOperand, newOperand);
644                        }
645                        else {
646                            pendingOperand = makeDiff(pendingOperand, newOperand);
647                        }
648                    }
649                    else {
650                        pendingOperand = newOperand;
651                    }
652                    subexprs.push_back(pendingOperand);
653                    havePendingOperation = false;
654                }
655                if (op == setOpener) {
656                    subexprs.push_back(parse_charset());
657                    lastItemKind = SetItem;
658                }
659                else if (op == posixPropertyOpener) {
660                    bool negated = false;
661                    if (*_cursor == '^') {
662                        negated = true;
663                        _cursor++;
664                    }
665                    RE * posixSet = parse_property_expression();
666                    if (negated) posixSet = makeComplement(posixSet);
667                    subexprs.push_back(posixSet);
668                    lastItemKind = BrackettedSetItem;
669                    if (_cursor == _end || *_cursor++ != ':' || _cursor == _end || *_cursor++ != ']')
670                        throw ParseFailure("Posix set expression improperly terminated.");
671                }
672            }
673            break;
674            case rangeHyphen:
675                if (lastItemKind != CodepointItem) throw ParseFailure("Range operator - has illegal left operand.");
676                cc->insert_range(lastCodepointItem, parse_codepoint());
677                lastItemKind = RangeItem;
678                break;
679            case hyphenChar:
680                cc->insert('-'); 
681                lastItemKind = CodepointItem;
682                lastCodepointItem = static_cast<codepoint_t> ('-');
683                break;
684            case ampChar:
685                cc->insert('&'); 
686                lastItemKind = CodepointItem;
687                lastCodepointItem = static_cast<codepoint_t> ('&');
688                break;
689            case backSlash:
690                throw_incomplete_expression_error_if_end_of_stream();
691                if (isSetEscapeChar(*_cursor)) {
692                    subexprs.push_back(parse_escaped_set());
693                    lastItemKind = SetItem;
694                }
695                else {
696                    lastCodepointItem = parse_escaped_codepoint();
697                    cc->insert(lastCodepointItem);
698                    lastItemKind = CodepointItem;
699                }
700                break;
701            case emptyOperator:
702                lastCodepointItem = parse_utf8_codepoint();
703                cc->insert(lastCodepointItem);
704                lastItemKind = CodepointItem;
705                break;
706        }
707    }
708    throw ParseFailure("Set expression not properly terminated.");
709}
710
711
712codepoint_t RE_Parser::parse_codepoint() {
713    if (_cursor != _end && *_cursor == '\\') {
714        _cursor++;
715        return parse_escaped_codepoint();
716    }
717    else {
718        return parse_utf8_codepoint();
719    }
720}
721
722// A backslash escape was found, and various special cases (back reference,
723// quoting with \Q, \E, sets (\p, \P, \d, \D, \w, \W, \s, \S, \b, \B), grapheme
724// cluster \X have been ruled out.
725// It may be one of several possibilities or an error sequence.
726// 1. Special control codes (\a, \e, \f, \n, \r, \t, \v)
727// 2. General control codes c[@-_a-z?]
728// 3. Restricted octal notation 0 - 0777
729// 4. General octal notation o\{[0-7]+\}
730// 5. General hex notation x\{[0-9A-Fa-f]+\}
731// 6. An error for any unrecognized alphabetic escape
732// 7. An escaped ASCII symbol, standing for itself
733
734codepoint_t RE_Parser::parse_escaped_codepoint() {
735    codepoint_t cp_value;
736    throw_incomplete_expression_error_if_end_of_stream();
737    switch (*_cursor) {
738        case 'a': ++_cursor; return 0x07; // BEL
739        case 'e': ++_cursor; return 0x1B; // ESC
740        case 'f': ++_cursor; return 0x0C; // FF
741        case 'n': ++_cursor; return 0x0A; // LF
742        case 'r': ++_cursor; return 0x0D; // CR
743        case 't': ++_cursor; return 0x09; // HT
744        case 'v': ++_cursor; return 0x0B; // VT
745        case 'c': // Control-escape based on next char
746            ++_cursor;
747            throw_incomplete_expression_error_if_end_of_stream();
748            // \c@, \cA, ... \c_, or \ca, ..., \cz
749            if (((*_cursor >= '@') && (*_cursor <= '_')) || ((*_cursor >= 'a') && (*_cursor <= 'z'))) {
750                cp_value = static_cast<codepoint_t>(*_cursor & 0x1F);
751                _cursor++;
752                return cp_value;
753            }
754            else if (*_cursor++ == '?') return 0x7F;  // \c? ==> DEL
755            else throw("Illegal \\c escape sequence");
756        case '0': // Octal escape:  0 - 0377
757            ++_cursor;
758            return parse_octal_codepoint(0,3);
759        case 'o':
760            ++_cursor;
761            throw_incomplete_expression_error_if_end_of_stream();
762            if (*_cursor == '{') {
763                ++_cursor;
764                cp_value = parse_octal_codepoint(1, 7);
765                if (_cursor == _end || *_cursor++ != '}') throw ParseFailure("Malformed octal escape sequence");
766                return cp_value;
767            }
768            else {
769                throw ParseFailure("Malformed octal escape sequence");
770            }
771        case 'x':
772            ++_cursor;
773            throw_incomplete_expression_error_if_end_of_stream();
774            if (*_cursor == '{') {
775              ++_cursor;
776              cp_value = parse_hex_codepoint(1, 6);
777              if (_cursor == _end || *_cursor++ != '}') throw ParseFailure("Malformed hex escape sequence");
778              return cp_value;
779            }
780            else {
781                return parse_hex_codepoint(1,2);  // ICU compatibility
782            }
783        case 'u':
784            ++_cursor;
785            throw_incomplete_expression_error_if_end_of_stream();
786            if (*_cursor == '{') {
787                ++_cursor;
788                cp_value = parse_hex_codepoint(1, 6);
789                if (_cursor == _end || *_cursor++ != '}') throw ParseFailure("Malformed hex escape sequence");
790                return cp_value;
791            }
792            else {
793                return parse_hex_codepoint(4,4);  // ICU compatibility
794            }
795        case 'U':
796            ++_cursor;
797            return parse_hex_codepoint(8,8);  // ICU compatibility
798        case 'N':
799            ++_cursor;
800            throw ParseFailure("\\N{...} character name syntax not yet supported.");
801
802        default:
803            // Escaped letters should be reserved for special functions.
804            if (((*_cursor >= 'A') && (*_cursor <= 'Z')) || ((*_cursor >= 'a') && (*_cursor <= 'z')))
805                throw ParseFailure("Undefined or unsupported escape sequence");
806            else if ((*_cursor < 0x20) || (*_cursor >= 0x7F))
807                throw ParseFailure("Illegal escape sequence");
808            else return static_cast<codepoint_t>(*_cursor++);
809    }
810}
811
812
813codepoint_t RE_Parser::parse_octal_codepoint(int mindigits, int maxdigits) {
814    codepoint_t value = 0;
815    int count = 0;
816    while (_cursor != _end && count < maxdigits) {
817        const char t = *_cursor;
818        if (t < '0' || t > '7') {
819            break;
820        }
821        value = value * 8 | (t - '0');
822        ++_cursor;
823        ++count;
824    }
825    if (count < mindigits) throw ParseFailure("Octal sequence has too few digits");
826    if (value > CC::UNICODE_MAX) throw ParseFailure("Octal value too large");
827    return value;
828}
829
830codepoint_t RE_Parser::parse_hex_codepoint(int mindigits, int maxdigits) {
831    codepoint_t value = 0;
832    int count = 0;
833    while (_cursor != _end && isxdigit(*_cursor) && count < maxdigits) {
834        const char t = *_cursor;
835        if (isdigit(t)) {
836            value = (value * 16) | (t - '0');
837        }
838        else {   
839            value = (value * 16) | ((t | 32) - 'a') + 10;
840        }
841        ++_cursor;
842        ++count;
843    }
844    if (count < mindigits) throw ParseFailure("Hexadecimal sequence has too few digits");
845    if (value > CC::UNICODE_MAX) throw ParseFailure("Hexadecimal value too large");
846    return value;
847}
848
849
850inline void RE_Parser::throw_incomplete_expression_error_if_end_of_stream() const {
851    if (_cursor == _end) throw IncompleteRegularExpression();
852}
853
854CC * RE_Parser::build_CC(codepoint_t cp) {
855    CC * cc = makeCC();
856    CC_add_codepoint(cc, cp);
857    return cc;
858}
859
860void RE_Parser::CC_add_codepoint(CC * cc, codepoint_t cp) {
861    if (fModeFlagSet & CASE_INSENSITIVE_MODE_FLAG) {
862        caseInsensitiveInsert(cc, cp);
863    }
864    else cc->insert(cp);
865}
866
867void RE_Parser::CC_add_range(CC * cc, codepoint_t lo, codepoint_t hi) {
868    if (fModeFlagSet & CASE_INSENSITIVE_MODE_FLAG) {
869        caseInsensitiveInsertRange(cc, lo, hi);
870    }
871    else cc-> insert_range(lo, hi);
872}
873   
874   
875}
Note: See TracBrowser for help on using the repository browser.