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

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

Parsing support for lookahead/behind assertions, mode flags

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