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

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

Revise logic of word boundary tests

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