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

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

Fix UTF-8 parsing

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