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

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

Add fModeFlags into parsing object, set/save/restore with groups.

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