Skip to main content

resharp_parser/
lib.rs

1//! Parser for resharp regex patterns.
2//!
3//! Converts regex pattern strings into the node representation used by resharp-algebra.
4
5#![warn(dead_code)]
6pub mod ast;
7use std::cell::{Cell, RefCell};
8
9use ast::{Ast, Concat, ErrorKind, GroupKind, LookaroundKind, RepetitionKind};
10use regex_syntax::{
11    ast::{
12        ClassAscii, ClassBracketed, ClassPerl, ClassSet, ClassSetBinaryOpKind, ClassSetItem,
13        ClassSetRange, ClassSetUnion, ClassUnicode, ClassUnicodeKind, ClassUnicodeOpKind,
14        HexLiteralKind, Literal, LiteralKind, Position, Span, SpecialLiteralKind,
15    },
16    hir::{
17        self,
18        translate::{Translator, TranslatorBuilder},
19    },
20    utf8::Utf8Sequences,
21};
22use resharp_algebra::{Kind, NodeId};
23
24type TB<'s> = resharp_algebra::RegexBuilder;
25
26/// global pattern-level flags, set from `RegexOptions`.
27pub struct PatternFlags {
28    /// `\w`/`\d`/`\s` match full Unicode (true) or ASCII only (false).
29    pub unicode: bool,
30    /// `\w` covers all Unicode word chars including 3- and 4-byte sequences.
31    pub full_unicode: bool,
32    /// global case-insensitive matching.
33    pub case_insensitive: bool,
34    /// `.` matches `\n` (behaves like `_`).
35    pub dot_matches_new_line: bool,
36    /// `^` and `$` match at line boundaries (`\n`) in addition to text boundaries.
37    pub multiline: bool,
38    /// allow whitespace and `#` comments in the pattern.
39    pub ignore_whitespace: bool,
40    /// ASCII `\w`/`\d`/`\s` tables, but
41    /// negated perl classes (`\W`/`\D`/`\S`) and `.` match a full codepoint
42    pub ascii_perl_classes: bool,
43    /// max upper bound on `expanded_ast_size` before parser rejects the
44    /// pattern as too complex. default 50_000.
45    pub expanded_ast_limit: u64,
46    /// max children allowed in any single `Concat`/`Alternation`/
47    /// `Intersection` node. default 4_000.
48    pub max_list_len: usize,
49    /// max upper bound on bounded repetition `{n,m}`. default 500.
50    pub max_repeat: u32,
51    pub max_depth: usize,
52}
53
54// arbitrary safeguards, these will not prevent intentional DoS patterns
55// more to protect you from shooting yourself in the foot
56pub const DEFAULT_MAX_REPEAT: u32 = 500;
57pub const DEFAULT_EXPANDED_AST_LIMIT: u64 = 50_000;
58pub const DEFAULT_MAX_LIST_LEN: usize = 4_000;
59pub const DEFAULT_MAX_DEPTH: usize = 1_000;
60
61impl Default for PatternFlags {
62    fn default() -> Self {
63        Self {
64            unicode: true,
65            full_unicode: false,
66            case_insensitive: false,
67            dot_matches_new_line: false,
68            multiline: true,
69            ignore_whitespace: false,
70            ascii_perl_classes: false,
71            expanded_ast_limit: DEFAULT_EXPANDED_AST_LIMIT,
72            max_list_len: DEFAULT_MAX_LIST_LEN,
73            max_repeat: DEFAULT_MAX_REPEAT,
74            max_depth: DEFAULT_MAX_DEPTH,
75        }
76    }
77}
78
79#[derive(Clone, Copy, PartialEq, Debug)]
80enum WordCharKind {
81    Word,
82    NonWord,
83    MaybeWord,
84    MaybeNonWord,
85    Unknown,
86    Edge,
87}
88
89fn is_word_byte(b: u8) -> bool {
90    b.is_ascii_alphanumeric() || b == b'_'
91}
92
93fn class_set_item_word_kind(item: &regex_syntax::ast::ClassSetItem) -> WordCharKind {
94    use regex_syntax::ast::{ClassPerlKind, ClassSetItem};
95    use WordCharKind::*;
96    match item {
97        ClassSetItem::Empty(_) => Unknown,
98        ClassSetItem::Literal(l) => {
99            if is_word_byte(l.c as u8) {
100                Word
101            } else {
102                NonWord
103            }
104        }
105        ClassSetItem::Range(r) => {
106            let all_word = (r.start.c as u8..=r.end.c as u8).all(is_word_byte);
107            let all_non = (r.start.c as u8..=r.end.c as u8).all(|b| !is_word_byte(b));
108            if all_word {
109                Word
110            } else if all_non {
111                NonWord
112            } else {
113                Unknown
114            }
115        }
116        ClassSetItem::Perl(p) => match (&p.kind, p.negated) {
117            (ClassPerlKind::Word, false) => Word,
118            (ClassPerlKind::Word, true) => NonWord,
119            (ClassPerlKind::Space, false) => NonWord,
120            (ClassPerlKind::Digit, false) => Word,
121            _ => Unknown,
122        },
123        ClassSetItem::Bracketed(b) => class_bracketed_word_kind(b),
124        ClassSetItem::Union(u) => {
125            let mut kind = Unknown;
126            for item in &u.items {
127                let k = class_set_item_word_kind(item);
128                kind = match (kind, k) {
129                    (_, Unknown) => return Unknown,
130                    (Unknown, _) => k,
131                    (Word, Word) => Word,
132                    (NonWord, NonWord) => NonWord,
133                    _ => return Unknown,
134                };
135            }
136            kind
137        }
138        _ => Unknown,
139    }
140}
141
142fn utf8_codepoint_node(tb: &mut TB<'_>) -> NodeId {
143    let ascii = tb.mk_range_u8(0, 127);
144    let beta = tb.mk_range_u8(0x80, 0xBF);
145    let c0 = tb.mk_range_u8(0xC0, 0xDF);
146    let c0s = tb.mk_concats([c0, beta].into_iter());
147    let e0 = tb.mk_range_u8(0xE0, 0xEF);
148    let e0s = tb.mk_concats([e0, beta, beta].into_iter());
149    let f0 = tb.mk_range_u8(0xF0, 0xF7);
150    let f0s = tb.mk_concats([f0, beta, beta, beta].into_iter());
151    tb.mk_unions([ascii, c0s, e0s, f0s].into_iter())
152}
153
154fn unicode_ranges_to_node(ranges: &[hir::ClassUnicodeRange], tb: &mut TB<'_>) -> NodeId {
155    let mut chains: Vec<Vec<(u8, u8)>> = Vec::new();
156    for range in ranges {
157        for seq in Utf8Sequences::new(range.start(), range.end()) {
158            let sl = seq.as_slice();
159            chains.push(sl.iter().map(|s| (s.start, s.end)).collect());
160        }
161    }
162    build_utf8_chain_trie(&chains, tb)
163}
164
165fn build_utf8_chain_trie(chains: &[Vec<(u8, u8)>], tb: &mut TB<'_>) -> NodeId {
166    if chains.is_empty() {
167        return NodeId::BOT;
168    }
169    let mut leaf_set_id: Option<resharp_algebra::solver::TSetId> = None;
170    let mut rest: Vec<&Vec<(u8, u8)>> = Vec::new();
171    for c in chains {
172        if c.len() == 1 {
173            let rid = tb.solver().range_to_set_id(c[0].0, c[0].1);
174            leaf_set_id = Some(match leaf_set_id {
175                None => rid,
176                Some(acc) => tb.solver().or_id(acc, rid),
177            });
178        } else {
179            rest.push(c);
180        }
181    }
182    let mut groups: Vec<((u8, u8), Vec<Vec<(u8, u8)>>)> = Vec::new();
183    for c in &rest {
184        match groups.last_mut() {
185            Some((head, tails)) if *head == c[0] => tails.push(c[1..].to_vec()),
186            _ => groups.push((c[0], vec![c[1..].to_vec()])),
187        }
188    }
189    let mut nodes = Vec::with_capacity(groups.len() + 1);
190    if let Some(set_id) = leaf_set_id {
191        nodes.push(tb.mk_pred_from_set(set_id));
192    }
193    for (head, tails) in &groups {
194        let tail_node = build_utf8_chain_trie(tails, tb);
195        let head_node = tb.mk_range_u8(head.0, head.1);
196        nodes.push(tb.mk_concat(head_node, tail_node));
197    }
198    tb.mk_unions(nodes.into_iter())
199}
200
201fn class_bracketed_word_kind(c: &regex_syntax::ast::ClassBracketed) -> WordCharKind {
202    use regex_syntax::ast::{ClassPerlKind, ClassSet, ClassSetItem};
203    use WordCharKind::*;
204    if c.negated {
205        return match &c.kind {
206            ClassSet::Item(ClassSetItem::Perl(p)) if p.kind == ClassPerlKind::Word => {
207                if p.negated {
208                    Word
209                } else {
210                    NonWord
211                }
212            }
213            _ => Unknown,
214        };
215    }
216    match &c.kind {
217        ClassSet::Item(item) => class_set_item_word_kind(item),
218        ClassSet::BinaryOp(_) => Unknown,
219    }
220}
221
222fn ascii_class_lit(span: Span, c: char) -> regex_syntax::ast::Literal {
223    regex_syntax::ast::Literal {
224        span,
225        kind: regex_syntax::ast::LiteralKind::Verbatim,
226        c,
227    }
228}
229
230fn ascii_class_range(span: Span, a: char, b: char) -> regex_syntax::ast::ClassSetItem {
231    regex_syntax::ast::ClassSetItem::Range(regex_syntax::ast::ClassSetRange {
232        span,
233        start: ascii_class_lit(span, a),
234        end: ascii_class_lit(span, b),
235    })
236}
237
238fn ascii_perl_positive(
239    span: Span,
240    kind: &regex_syntax::ast::ClassPerlKind,
241) -> regex_syntax::ast::ClassSetItem {
242    use regex_syntax::ast::{ClassPerlKind, ClassSetItem, ClassSetUnion};
243    match kind {
244        ClassPerlKind::Digit => ascii_class_range(span, '0', '9'),
245        ClassPerlKind::Word => ClassSetItem::Union(ClassSetUnion {
246            span,
247            items: vec![
248                ascii_class_range(span, 'a', 'z'),
249                ascii_class_range(span, 'A', 'Z'),
250                ascii_class_range(span, '0', '9'),
251                ClassSetItem::Literal(ascii_class_lit(span, '_')),
252            ],
253        }),
254        ClassPerlKind::Space => ClassSetItem::Union(ClassSetUnion {
255            span,
256            items: ['\t', '\n', '\x0B', '\x0C', '\r', ' ']
257                .into_iter()
258                .map(|c| ClassSetItem::Literal(ascii_class_lit(span, c)))
259                .collect(),
260        }),
261    }
262}
263
264fn ascii_perl_set_item(
265    span: Span,
266    kind: &regex_syntax::ast::ClassPerlKind,
267    negated: bool,
268) -> regex_syntax::ast::ClassSetItem {
269    use regex_syntax::ast::{ClassBracketed, ClassSet, ClassSetItem};
270    let positive = ascii_perl_positive(span, kind);
271    if negated {
272        ClassSetItem::Bracketed(Box::new(ClassBracketed {
273            span,
274            negated: true,
275            kind: ClassSet::Item(positive),
276        }))
277    } else {
278        positive
279    }
280}
281
282fn rewrite_ascii_perl_set(set: &regex_syntax::ast::ClassSet) -> regex_syntax::ast::ClassSet {
283    use regex_syntax::ast::{ClassSet, ClassSetBinaryOp};
284    match set {
285        ClassSet::Item(item) => ClassSet::Item(rewrite_ascii_perl_item(item)),
286        ClassSet::BinaryOp(op) => ClassSet::BinaryOp(ClassSetBinaryOp {
287            span: op.span,
288            kind: op.kind.clone(),
289            lhs: Box::new(rewrite_ascii_perl_set(&op.lhs)),
290            rhs: Box::new(rewrite_ascii_perl_set(&op.rhs)),
291        }),
292    }
293}
294
295fn rewrite_ascii_perl_item(
296    item: &regex_syntax::ast::ClassSetItem,
297) -> regex_syntax::ast::ClassSetItem {
298    use regex_syntax::ast::{ClassBracketed, ClassSetItem, ClassSetUnion};
299    match item {
300        ClassSetItem::Perl(p) => ascii_perl_set_item(p.span, &p.kind, p.negated),
301        ClassSetItem::Union(u) => ClassSetItem::Union(ClassSetUnion {
302            span: u.span,
303            items: u.items.iter().map(rewrite_ascii_perl_item).collect(),
304        }),
305        ClassSetItem::Bracketed(b) => ClassSetItem::Bracketed(Box::new(ClassBracketed {
306            span: b.span,
307            negated: b.negated,
308            kind: rewrite_ascii_perl_set(&b.kind),
309        })),
310        other => other.clone(),
311    }
312}
313
314#[derive(Clone, Debug, Eq, PartialEq)]
315enum Primitive {
316    Literal(Literal),
317    Assertion(ast::Assertion),
318    Dot(Span),
319    Top(Span),
320    Perl(ClassPerl),
321    Unicode(ClassUnicode),
322}
323
324impl Primitive {
325    fn span(&self) -> &Span {
326        match *self {
327            Primitive::Literal(ref x) => &x.span,
328            Primitive::Assertion(ref x) => &x.span,
329            Primitive::Dot(ref span) => span,
330            Primitive::Top(ref span) => span,
331            Primitive::Perl(ref x) => &x.span,
332            Primitive::Unicode(ref x) => &x.span,
333        }
334    }
335
336    fn into_ast(self) -> Ast {
337        match self {
338            Primitive::Literal(lit) => Ast::literal(lit),
339            Primitive::Assertion(assert) => Ast::assertion(assert),
340            Primitive::Dot(span) => Ast::dot(span),
341            Primitive::Top(span) => Ast::top(span),
342            Primitive::Perl(cls) => Ast::class_perl(cls),
343            Primitive::Unicode(cls) => Ast::class_unicode(cls),
344        }
345    }
346
347    fn into_class_set_item(self, p: &ResharpParser) -> Result<regex_syntax::ast::ClassSetItem> {
348        use self::Primitive::*;
349        use regex_syntax::ast::ClassSetItem;
350
351        match self {
352            Literal(lit) => Ok(ClassSetItem::Literal(lit)),
353            Perl(cls) => Ok(ClassSetItem::Perl(cls)),
354            Unicode(cls) => Ok(ClassSetItem::Unicode(cls)),
355            x => Err(p.error(*x.span(), ast::ErrorKind::ClassEscapeInvalid)),
356        }
357    }
358
359    fn into_class_literal(self, p: &ResharpParser) -> Result<Literal> {
360        use self::Primitive::*;
361
362        match self {
363            Literal(lit) => Ok(lit),
364            x => Err(p.error(*x.span(), ast::ErrorKind::ClassRangeLiteral)),
365        }
366    }
367}
368
369#[derive(Clone, Debug, Eq, PartialEq)]
370pub enum Either<Left, Right> {
371    Left(Left),
372    Right(Right),
373}
374
375#[derive(Clone, Debug, Eq, PartialEq)]
376pub struct ParseError {
377    /// The kind of error.
378    pub kind: ErrorKind,
379    /// The original pattern that the parser generated the error from. Every
380    /// span in an error is a valid range into this string.
381    pattern: String,
382    /// The span of this error.
383    pub span: Span,
384}
385
386impl std::fmt::Display for ParseError {
387    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
388        write!(f, "{:?}: {:?}", self.kind, self.span)
389    }
390}
391impl std::error::Error for ParseError {}
392
393type Result<T> = core::result::Result<T, ParseError>;
394
395#[derive(Clone, Debug)]
396enum GroupState {
397    /// This state is pushed whenever an opening group is found.
398    Group {
399        /// The concatenation immediately preceding the opening group.
400        concat: Concat,
401        /// The group that has been opened. Its sub-AST is always empty.
402        group: ast::Group,
403        /// Whether this group has the `x` flag enabled or not.
404        ignore_whitespace: bool,
405    },
406    Alternation(ast::Alternation),
407    Intersection(ast::Intersection),
408}
409
410#[derive(Clone, Debug)]
411enum ClassState {
412    /// This state is pushed whenever an opening bracket is found.
413    Open {
414        /// The union of class items immediately preceding this class.
415        union: regex_syntax::ast::ClassSetUnion,
416        set: regex_syntax::ast::ClassBracketed,
417    },
418    /// This state is pushed when a operator is seen. When popped, the stored
419    /// set becomes the left hand side of the operator.
420    Op {
421        /// The type of the operation, i.e., &&, -- or ~~.
422        kind: regex_syntax::ast::ClassSetBinaryOpKind,
423        /// The left-hand side of the operator.
424        lhs: regex_syntax::ast::ClassSet,
425    },
426}
427
428/// RE# syntax parser based on the regex-syntax crate.
429pub struct ResharpParser<'s> {
430    perl_classes: Vec<(bool, regex_syntax::ast::ClassPerlKind, NodeId)>,
431    unicode_classes: resharp_algebra::UnicodeClassCache,
432    pub translator: regex_syntax::hir::translate::Translator,
433    pub pattern: &'s str,
434    pos: Cell<Position>,
435    capture_index: Cell<u32>,
436    octal: bool,
437    empty_min_range: bool,
438    ignore_whitespace: Cell<bool>,
439    dot_all: Cell<bool>,
440    multiline: Cell<bool>,
441    global_unicode: bool,
442    global_full_unicode: bool,
443    global_ascii_perl: bool,
444    global_case_insensitive: bool,
445    expanded_ast_limit: u64,
446    max_list_len: usize,
447    max_repeat: u32,
448    max_depth: usize,
449    comments: RefCell<Vec<ast::Comment>>,
450    stack_group: RefCell<Vec<GroupState>>,
451    stack_class: RefCell<Vec<ClassState>>,
452    capture_names: RefCell<Vec<ast::CaptureName>>,
453    scratch: RefCell<String>,
454}
455
456fn specialize_err<T>(result: Result<T>, from: ast::ErrorKind, to: ast::ErrorKind) -> Result<T> {
457    result.map_err(|e| {
458        if e.kind == from {
459            ParseError {
460                kind: to,
461                pattern: e.pattern,
462                span: e.span,
463            }
464        } else {
465            e
466        }
467    })
468}
469
470fn is_capture_char(c: char, first: bool) -> bool {
471    if first {
472        c == '_' || c.is_alphabetic()
473    } else {
474        c == '_' || c == '.' || c == '[' || c == ']' || c.is_alphanumeric()
475    }
476}
477
478pub fn is_meta_character(c: char) -> bool {
479    matches!(
480        c,
481        '\\' | '.'
482            | '+'
483            | '*'
484            | '?'
485            | '('
486            | ')'
487            | '|'
488            | '['
489            | ']'
490            | '{'
491            | '}'
492            | '^'
493            | '$'
494            | '#'
495            | '&'
496            | '-'
497            | '~'
498            | '_'
499    )
500}
501
502/// escapes all resharp meta characters in `text`.
503pub fn escape(text: &str) -> String {
504    let mut buf = String::new();
505    escape_into(text, &mut buf);
506    buf
507}
508
509/// escapes all resharp meta characters in `text` and appends to `buf`.
510pub fn escape_into(text: &str, buf: &mut String) {
511    buf.reserve(text.len());
512    for c in text.chars() {
513        if is_meta_character(c) {
514            buf.push('\\');
515        }
516        buf.push(c);
517    }
518}
519
520pub fn is_escapeable_character(c: char) -> bool {
521    if is_meta_character(c) {
522        return true;
523    }
524    if !c.is_ascii() {
525        return false;
526    }
527    match c {
528        '0'..='9' | 'A'..='Z' | 'a'..='z' => false,
529        '<' | '>' => false,
530        _ => true,
531    }
532}
533
534fn is_hex(c: char) -> bool {
535    c.is_ascii_digit() || ('a'..='f').contains(&c) || ('A'..='F').contains(&c)
536}
537
538fn ensure_lookbehind_at_start(ast: &Ast, at_start: bool) -> core::result::Result<(), Span> {
539    match ast {
540        Ast::Concat(c) => {
541            let mut child_at_start = at_start;
542            for child in &c.asts {
543                ensure_lookbehind_at_start(child, child_at_start)?;
544                if ast_may_consume(child) {
545                    child_at_start = false;
546                }
547            }
548            Ok(())
549        }
550        Ast::Alternation(a) => {
551            for child in &a.asts {
552                ensure_lookbehind_at_start(child, at_start)?;
553            }
554            Ok(())
555        }
556        Ast::Intersection(i) => {
557            for child in &i.asts {
558                ensure_lookbehind_at_start(child, at_start)?;
559            }
560            Ok(())
561        }
562        Ast::Complement(c) => ensure_lookbehind_at_start(&c.ast, at_start),
563        Ast::Group(g) => ensure_lookbehind_at_start(&g.ast, at_start),
564        Ast::Repetition(r) => ensure_lookbehind_at_start(&r.ast, at_start),
565        Ast::Lookaround(g) => {
566            match g.kind {
567                LookaroundKind::PositiveLookbehind | LookaroundKind::NegativeLookbehind => {
568                    if !at_start {
569                        return Err(g.span);
570                    }
571                }
572                LookaroundKind::PositiveLookahead | LookaroundKind::NegativeLookahead => {}
573            }
574            ensure_lookbehind_at_start(&g.ast, true)
575        }
576        Ast::Empty(_)
577        | Ast::Flags(_)
578        | Ast::Literal(_)
579        | Ast::Dot(_)
580        | Ast::Top(_)
581        | Ast::Assertion(_)
582        | Ast::ClassUnicode(_)
583        | Ast::ClassPerl(_)
584        | Ast::ClassBracketed(_) => Ok(()),
585    }
586}
587
588fn ast_may_consume(ast: &Ast) -> bool {
589    match ast {
590        Ast::Empty(_) | Ast::Flags(_) | Ast::Assertion(_) | Ast::Lookaround(_) => false,
591        Ast::Literal(_)
592        | Ast::Dot(_)
593        | Ast::Top(_)
594        | Ast::ClassUnicode(_)
595        | Ast::ClassPerl(_)
596        | Ast::ClassBracketed(_) => true,
597        Ast::Group(g) => ast_may_consume(&g.ast),
598        Ast::Repetition(r) => {
599            if !ast_may_consume(&r.ast) {
600                return false;
601            }
602            match r.op.kind {
603                RepetitionKind::ZeroOrOne
604                | RepetitionKind::ZeroOrMore
605                | RepetitionKind::OneOrMore => true,
606                RepetitionKind::Range(ast::RepetitionRange::Exactly(0)) => false,
607                RepetitionKind::Range(ast::RepetitionRange::Bounded(_, 0)) => false,
608                RepetitionKind::Range(_) => true,
609            }
610        }
611        Ast::Alternation(a) => a.asts.iter().any(ast_may_consume),
612        Ast::Intersection(i) => i.asts.iter().any(ast_may_consume),
613        Ast::Complement(_) => true,
614        Ast::Concat(c) => c.asts.iter().any(ast_may_consume),
615    }
616}
617
618impl<'s> ResharpParser<'s> {
619    fn default_translator_builder(&self) -> TranslatorBuilder {
620        let mut trb = TranslatorBuilder::new();
621        trb.unicode(self.global_unicode);
622        trb.utf8(false);
623        trb.case_insensitive(self.global_case_insensitive);
624        trb
625    }
626
627    pub fn new(pattern: &'s str) -> Self {
628        Self::with_flags(pattern, &PatternFlags::default())
629    }
630
631    pub fn with_flags(pattern: &'s str, flags: &PatternFlags) -> Self {
632        let mut trb = TranslatorBuilder::new();
633        trb.unicode(flags.unicode);
634        trb.utf8(false);
635        trb.case_insensitive(flags.case_insensitive);
636        Self {
637            translator: trb.build(),
638            pattern,
639            perl_classes: vec![],
640            unicode_classes: resharp_algebra::UnicodeClassCache::default(),
641            pos: Cell::new(Position::new(0, 0, 0)),
642            capture_index: Cell::new(0),
643            octal: false,
644            empty_min_range: false,
645            ignore_whitespace: Cell::new(flags.ignore_whitespace),
646            dot_all: Cell::new(flags.dot_matches_new_line),
647            multiline: Cell::new(flags.multiline),
648            global_unicode: flags.unicode || flags.full_unicode || flags.ascii_perl_classes,
649            global_full_unicode: flags.full_unicode,
650            global_ascii_perl: flags.ascii_perl_classes,
651            global_case_insensitive: flags.case_insensitive,
652            expanded_ast_limit: flags.expanded_ast_limit,
653            max_list_len: flags.max_list_len,
654            max_repeat: flags.max_repeat,
655            max_depth: flags.max_depth,
656            comments: RefCell::new(vec![]),
657            stack_group: RefCell::new(vec![]),
658            stack_class: RefCell::new(vec![]),
659            capture_names: RefCell::new(vec![]),
660            scratch: RefCell::new(String::new()),
661        }
662    }
663
664    fn parser(&'_ self) -> &'_ ResharpParser<'_> {
665        self
666    }
667
668    fn pattern(&self) -> &str {
669        self.pattern
670    }
671
672    fn error(&self, span: Span, kind: ast::ErrorKind) -> ParseError {
673        ParseError {
674            kind,
675            pattern: self.pattern().to_string(),
676            span,
677        }
678    }
679
680    fn unsupported_error(&self, _: regex_syntax::hir::Error) -> ParseError {
681        self.error(
682            Span::splat(self.pos()),
683            ast::ErrorKind::UnsupportedResharpRegex,
684        )
685    }
686
687    fn offset(&self) -> usize {
688        self.parser().pos.get().offset
689    }
690
691    fn line(&self) -> usize {
692        self.parser().pos.get().line
693    }
694
695    fn column(&self) -> usize {
696        self.parser().pos.get().column
697    }
698
699    fn next_capture_index(&self, span: Span) -> Result<u32> {
700        let current = self.parser().capture_index.get();
701        let i = current
702            .checked_add(1)
703            .ok_or_else(|| self.error(span, ast::ErrorKind::CaptureLimitExceeded))?;
704        self.parser().capture_index.set(i);
705        Ok(i)
706    }
707
708    fn add_capture_name(&self, cap: &ast::CaptureName) -> Result<()> {
709        let mut names = self.parser().capture_names.borrow_mut();
710        match names.binary_search_by_key(&cap.name.as_str(), |c| c.name.as_str()) {
711            Err(i) => {
712                names.insert(i, cap.clone());
713                Ok(())
714            }
715            Ok(i) => Err(self.error(
716                cap.span,
717                ast::ErrorKind::GroupNameDuplicate {
718                    original: names[i].span,
719                },
720            )),
721        }
722    }
723
724    fn ignore_whitespace(&self) -> bool {
725        self.parser().ignore_whitespace.get()
726    }
727
728    fn char(&self) -> char {
729        self.char_at(self.offset())
730    }
731
732    fn char_at(&self, i: usize) -> char {
733        self.pattern()[i..]
734            .chars()
735            .next()
736            .unwrap_or_else(|| panic!("expected char at offset {}", i))
737    }
738
739    fn bump(&self) -> bool {
740        if self.is_eof() {
741            return false;
742        }
743        let Position {
744            mut offset,
745            mut line,
746            mut column,
747        } = self.pos();
748        if self.char() == '\n' {
749            line = line.checked_add(1).unwrap();
750            column = 1;
751        } else {
752            column = column.checked_add(1).unwrap();
753        }
754        offset += self.char().len_utf8();
755        self.parser().pos.set(Position {
756            offset,
757            line,
758            column,
759        });
760        self.pattern()[self.offset()..].chars().next().is_some()
761    }
762
763    fn bump_if(&self, prefix: &str) -> bool {
764        if self.pattern()[self.offset()..].starts_with(prefix) {
765            for _ in 0..prefix.chars().count() {
766                self.bump();
767            }
768            true
769        } else {
770            false
771        }
772    }
773
774    fn is_lookaround_prefix(&self) -> Option<(bool, bool)> {
775        if self.bump_if("?=") {
776            return Some((true, true));
777        }
778        if self.bump_if("?!") {
779            return Some((true, false));
780        }
781        if self.bump_if("?<=") {
782            return Some((false, true));
783        }
784        if self.bump_if("?<!") {
785            return Some((false, false));
786        }
787        None
788    }
789
790    fn bump_and_bump_space(&self) -> bool {
791        if !self.bump() {
792            return false;
793        }
794        self.bump_space();
795        !self.is_eof()
796    }
797
798    fn bump_space(&self) {
799        if !self.ignore_whitespace() {
800            return;
801        }
802        while !self.is_eof() {
803            if self.char().is_whitespace() {
804                self.bump();
805            } else if self.char() == '#' {
806                let start = self.pos();
807                let mut comment_text = String::new();
808                self.bump();
809                while !self.is_eof() {
810                    let c = self.char();
811                    self.bump();
812                    if c == '\n' {
813                        break;
814                    }
815                    comment_text.push(c);
816                }
817                let comment = ast::Comment {
818                    span: Span::new(start, self.pos()),
819                    comment: comment_text,
820                };
821                self.parser().comments.borrow_mut().push(comment);
822            } else {
823                break;
824            }
825        }
826    }
827
828    fn peek(&self) -> Option<char> {
829        if self.is_eof() {
830            return None;
831        }
832        self.pattern()[self.offset() + self.char().len_utf8()..]
833            .chars()
834            .next()
835    }
836
837    /// Like peek, but will ignore spaces when the parser is in whitespace
838    /// insensitive mode.
839    fn peek_space(&self) -> Option<char> {
840        if !self.ignore_whitespace() {
841            return self.peek();
842        }
843        if self.is_eof() {
844            return None;
845        }
846        let mut start = self.offset() + self.char().len_utf8();
847        let mut in_comment = false;
848        for (i, c) in self.pattern()[start..].char_indices() {
849            if c.is_whitespace() {
850                continue;
851            } else if !in_comment && c == '#' {
852                in_comment = true;
853            } else if in_comment && c == '\n' {
854                in_comment = false;
855            } else {
856                start += i;
857                break;
858            }
859        }
860        self.pattern()[start..].chars().next()
861    }
862
863    fn is_eof(&self) -> bool {
864        self.offset() == self.pattern().len()
865    }
866
867    fn pos(&self) -> Position {
868        self.parser().pos.get()
869    }
870
871    fn span(&self) -> Span {
872        Span::splat(self.pos())
873    }
874
875    fn span_char(&self) -> Span {
876        let mut next = Position {
877            offset: self.offset().checked_add(self.char().len_utf8()).unwrap(),
878            line: self.line(),
879            column: self.column().checked_add(1).unwrap(),
880        };
881        if self.char() == '\n' {
882            next.line += 1;
883            next.column = 1;
884        }
885        Span::new(self.pos(), next)
886    }
887
888    #[inline(never)]
889    fn push_alternate(&self, mut concat: ast::Concat) -> Result<ast::Concat> {
890        assert_eq!(self.char(), '|');
891        concat.span.end = self.pos();
892        self.push_or_add_alternation(concat);
893        self.bump();
894        Ok(ast::Concat {
895            span: self.span(),
896            asts: vec![],
897        })
898    }
899
900    fn push_or_add_alternation(&self, concat: Concat) {
901        use self::GroupState::*;
902
903        let mut stack = self.parser().stack_group.borrow_mut();
904        if let Some(&mut Alternation(ref mut alts)) = stack.last_mut() {
905            alts.asts.push(concat.into_ast());
906            return;
907        }
908        stack.push(Alternation(ast::Alternation {
909            span: Span::new(concat.span.start, self.pos()),
910            asts: vec![concat.into_ast()],
911        }));
912    }
913
914    #[inline(never)]
915    fn push_intersect(&self, mut concat: Concat) -> Result<Concat> {
916        assert_eq!(self.char(), '&');
917        concat.span.end = self.pos();
918        self.push_or_add_intersect(concat);
919        self.bump();
920        Ok(Concat {
921            span: self.span(),
922            asts: vec![],
923        })
924    }
925
926    fn push_or_add_intersect(&self, concat: Concat) {
927        use self::GroupState::*;
928
929        let mut stack = self.parser().stack_group.borrow_mut();
930        if let Some(&mut Intersection(ref mut alts)) = stack.last_mut() {
931            alts.asts.push(concat.into_ast());
932            return;
933        }
934        stack.push(Intersection(ast::Intersection {
935            span: Span::new(concat.span.start, self.pos()),
936            asts: vec![concat.into_ast()],
937        }));
938    }
939
940    #[inline(never)]
941    fn push_group(&self, mut concat: Concat) -> Result<Concat> {
942        assert_eq!(self.char(), '(');
943        match self.parse_group()? {
944            Either::Left(set) => {
945                let ignore = set.flags.flag_state(ast::Flag::IgnoreWhitespace);
946                if let Some(v) = ignore {
947                    self.parser().ignore_whitespace.set(v);
948                }
949
950                concat.asts.push(Ast::flags(set));
951                Ok(concat)
952            }
953            Either::Right(group) => {
954                let old_ignore_whitespace = self.ignore_whitespace();
955                let new_ignore_whitespace = group
956                    .flags()
957                    .and_then(|f| f.flag_state(ast::Flag::IgnoreWhitespace))
958                    .unwrap_or(old_ignore_whitespace);
959                self.parser()
960                    .stack_group
961                    .borrow_mut()
962                    .push(GroupState::Group {
963                        concat,
964                        group,
965                        ignore_whitespace: old_ignore_whitespace,
966                    });
967                self.parser().ignore_whitespace.set(new_ignore_whitespace);
968                Ok(Concat {
969                    span: self.span(),
970                    asts: vec![],
971                })
972            }
973        }
974    }
975
976    #[inline(never)]
977    fn push_compl_group(&self, concat: Concat) -> Result<Concat> {
978        assert_eq!(self.char(), '~');
979        self.bump();
980        if self.is_eof() || self.char() != '(' {
981            return Err(self.error(self.span(), ast::ErrorKind::ComplementGroupExpected));
982        }
983        let open_span = self.span_char();
984        self.bump();
985        let group = ast::Group {
986            span: open_span,
987            kind: ast::GroupKind::Complement,
988            ast: Box::new(Ast::empty(self.span())),
989        };
990
991        let old_ignore_whitespace = self.ignore_whitespace();
992        let new_ignore_whitespace = group
993            .flags()
994            .and_then(|f| f.flag_state(ast::Flag::IgnoreWhitespace))
995            .unwrap_or(old_ignore_whitespace);
996        self.parser()
997            .stack_group
998            .borrow_mut()
999            .push(GroupState::Group {
1000                concat,
1001                group,
1002                ignore_whitespace: old_ignore_whitespace,
1003            });
1004        self.parser().ignore_whitespace.set(new_ignore_whitespace);
1005        Ok(Concat {
1006            span: self.span(),
1007            asts: vec![],
1008        })
1009    }
1010
1011    #[inline(never)]
1012    fn pop_group(&self, mut group_concat: Concat) -> Result<Concat> {
1013        use self::GroupState::*;
1014        assert_eq!(self.char(), ')');
1015        let mut stack = self.parser().stack_group.borrow_mut();
1016        let topstack = stack.pop();
1017
1018        let (mut prior_concat, mut group, ignore_whitespace, alt) = match topstack {
1019            Some(Group {
1020                concat,
1021                group,
1022                ignore_whitespace,
1023            }) => (concat, group, ignore_whitespace, None),
1024            Some(Alternation(alt)) => match stack.pop() {
1025                Some(Group {
1026                    concat,
1027                    group,
1028                    ignore_whitespace,
1029                }) => (
1030                    concat,
1031                    group,
1032                    ignore_whitespace,
1033                    Some(Either::Left::<ast::Alternation, ast::Intersection>(alt)),
1034                ),
1035                None | Some(Alternation(_)) | Some(Intersection(_)) => {
1036                    return Err(self.error(self.span_char(), ast::ErrorKind::GroupUnopened));
1037                }
1038            },
1039            Some(Intersection(int)) => match stack.pop() {
1040                Some(Group {
1041                    concat,
1042                    group,
1043                    ignore_whitespace,
1044                }) => (
1045                    concat,
1046                    group,
1047                    ignore_whitespace,
1048                    Some(Either::Right::<ast::Alternation, ast::Intersection>(int)),
1049                ),
1050                None | Some(Alternation(_)) | Some(Intersection(_)) => {
1051                    return Err(self.error(self.span_char(), ast::ErrorKind::GroupUnopened));
1052                }
1053            },
1054
1055            None => {
1056                return Err(self.error(self.span_char(), ast::ErrorKind::GroupUnopened));
1057            }
1058        };
1059        self.parser().ignore_whitespace.set(ignore_whitespace);
1060        group_concat.span.end = self.pos();
1061        self.bump();
1062        group.span.end = self.pos();
1063        match alt {
1064            Some(Either::Left(mut alt)) => {
1065                alt.span.end = group_concat.span.end;
1066                alt.asts.push(group_concat.into_ast());
1067                group.ast = Box::new(alt.into_ast());
1068            }
1069            Some(Either::Right(mut int)) => {
1070                int.span.end = group_concat.span.end;
1071                int.asts.push(group_concat.into_ast());
1072                group.ast = Box::new(int.into_ast());
1073            }
1074            None => {
1075                group.ast = Box::new(group_concat.into_ast());
1076            }
1077        }
1078
1079        if group.kind == GroupKind::Complement {
1080            let complement = ast::Complement {
1081                span: self.span(),
1082                ast: group.ast,
1083            };
1084            prior_concat.asts.push(Ast::complement(complement));
1085        }
1086        // ignore groups for now
1087        else {
1088            prior_concat.asts.push(Ast::group(group));
1089        }
1090        Ok(prior_concat)
1091    }
1092
1093    #[inline(never)]
1094    fn pop_group_end(&self, mut concat: ast::Concat) -> Result<Ast> {
1095        concat.span.end = self.pos();
1096        let mut stack = self.parser().stack_group.borrow_mut();
1097        let ast = match stack.pop() {
1098            None => Ok(concat.into_ast()),
1099            Some(GroupState::Alternation(mut alt)) => {
1100                alt.span.end = self.pos();
1101                alt.asts.push(concat.into_ast());
1102                Ok(Ast::alternation(alt))
1103            }
1104            Some(GroupState::Intersection(mut int)) => {
1105                int.span.end = self.pos();
1106                int.asts.push(concat.into_ast());
1107
1108                Ok(Ast::intersection(int))
1109            }
1110            Some(GroupState::Group { group, .. }) => {
1111                return Err(self.error(group.span, ast::ErrorKind::GroupUnclosed));
1112            }
1113        };
1114        // If we try to pop again, there should be nothing.
1115        match stack.pop() {
1116            None => ast,
1117            Some(GroupState::Alternation(alt)) => {
1118                Err(self.error(alt.span, ast::ErrorKind::UnsupportedResharpRegex))
1119            }
1120            Some(GroupState::Intersection(int)) => {
1121                Err(self.error(int.span, ast::ErrorKind::UnsupportedResharpRegex))
1122            }
1123            Some(GroupState::Group { group, .. }) => {
1124                Err(self.error(group.span, ast::ErrorKind::GroupUnclosed))
1125            }
1126        }
1127    }
1128
1129    #[inline(never)]
1130    fn push_class_open(
1131        &self,
1132        parent_union: regex_syntax::ast::ClassSetUnion,
1133    ) -> Result<regex_syntax::ast::ClassSetUnion> {
1134        assert_eq!(self.char(), '[');
1135
1136        let (nested_set, nested_union) = self.parse_set_class_open()?;
1137        self.parser()
1138            .stack_class
1139            .borrow_mut()
1140            .push(ClassState::Open {
1141                union: parent_union,
1142                set: nested_set,
1143            });
1144        Ok(nested_union)
1145    }
1146
1147    #[inline(never)]
1148    fn pop_class(
1149        &self,
1150        nested_union: regex_syntax::ast::ClassSetUnion,
1151    ) -> Result<Either<regex_syntax::ast::ClassSetUnion, regex_syntax::ast::ClassBracketed>> {
1152        assert_eq!(self.char(), ']');
1153
1154        let item = regex_syntax::ast::ClassSet::Item(nested_union.into_item());
1155        let prevset = self.pop_class_op(item);
1156        let mut stack = self.parser().stack_class.borrow_mut();
1157        match stack.pop() {
1158            None => panic!("unexpected empty character class stack"),
1159            Some(ClassState::Op { .. }) => panic!("unexpected ClassState::Op"),
1160            Some(ClassState::Open { mut union, mut set }) => {
1161                self.bump();
1162                set.span.end = self.pos();
1163                set.kind = prevset;
1164                if stack.is_empty() {
1165                    Ok(Either::Right(set))
1166                } else {
1167                    union.push(regex_syntax::ast::ClassSetItem::Bracketed(Box::new(set)));
1168                    Ok(Either::Left(union))
1169                }
1170            }
1171        }
1172    }
1173
1174    #[inline(never)]
1175    fn unclosed_class_error(&self) -> ParseError {
1176        for state in self.parser().stack_class.borrow().iter().rev() {
1177            if let ClassState::Open { ref set, .. } = *state {
1178                return self.error(set.span, ast::ErrorKind::ClassUnclosed);
1179            }
1180        }
1181        panic!("no open character class found")
1182    }
1183
1184    #[inline(never)]
1185    fn push_class_op(
1186        &self,
1187        next_kind: regex_syntax::ast::ClassSetBinaryOpKind,
1188        next_union: regex_syntax::ast::ClassSetUnion,
1189    ) -> regex_syntax::ast::ClassSetUnion {
1190        let item = regex_syntax::ast::ClassSet::Item(next_union.into_item());
1191        let new_lhs = self.pop_class_op(item);
1192        self.parser().stack_class.borrow_mut().push(ClassState::Op {
1193            kind: next_kind,
1194            lhs: new_lhs,
1195        });
1196        regex_syntax::ast::ClassSetUnion {
1197            span: self.span(),
1198            items: vec![],
1199        }
1200    }
1201
1202    #[inline(never)]
1203    fn pop_class_op(&self, rhs: regex_syntax::ast::ClassSet) -> regex_syntax::ast::ClassSet {
1204        let mut stack = self.parser().stack_class.borrow_mut();
1205        let (kind, lhs) = match stack.pop() {
1206            Some(ClassState::Op { kind, lhs }) => (kind, lhs),
1207            Some(state @ ClassState::Open { .. }) => {
1208                stack.push(state);
1209                return rhs;
1210            }
1211            None => unreachable!(),
1212        };
1213        let span = Span::new(lhs.span().start, rhs.span().end);
1214        regex_syntax::ast::ClassSet::BinaryOp(regex_syntax::ast::ClassSetBinaryOp {
1215            span,
1216            kind,
1217            lhs: Box::new(lhs),
1218            rhs: Box::new(rhs),
1219        })
1220    }
1221
1222    fn any_codepoint_node(&self, tb: &mut TB<'_>) -> NodeId {
1223        utf8_codepoint_node(tb)
1224    }
1225
1226    fn hir_to_node_id(&self, hir: &hir::Hir, tb: &mut TB<'s>) -> Result<NodeId> {
1227        match hir.kind() {
1228            hir::HirKind::Empty => Ok(NodeId::EPS),
1229            hir::HirKind::Literal(l) => {
1230                if l.0.len() == 1 {
1231                    let node = tb.mk_u8(l.0[0]);
1232                    Ok(node)
1233                } else {
1234                    let ws: Vec<_> = l.0.iter().map(|l| tb.mk_u8(*l)).collect();
1235                    let conc = tb.mk_concats(ws.iter().copied());
1236                    Ok(conc)
1237                }
1238            }
1239            hir::HirKind::Class(class) => match class {
1240                hir::Class::Unicode(class_unicode) => {
1241                    Ok(unicode_ranges_to_node(class_unicode.ranges(), tb))
1242                }
1243                hir::Class::Bytes(class_bytes) => {
1244                    let ranges = class_bytes.ranges();
1245                    let mut result = NodeId::BOT;
1246                    for range in ranges {
1247                        let start = range.start();
1248                        let end = range.end();
1249                        let node = tb.mk_range_u8(start, end);
1250                        result = tb.mk_union(result, node);
1251                    }
1252                    Ok(result)
1253                }
1254            },
1255            hir::HirKind::Look(_) => Err(self.error(
1256                Span::splat(self.pos()),
1257                ast::ErrorKind::UnsupportedResharpRegex,
1258            )),
1259            hir::HirKind::Repetition(_) => Err(self.error(
1260                Span::splat(self.pos()),
1261                ast::ErrorKind::UnsupportedResharpRegex,
1262            )),
1263            hir::HirKind::Capture(_) => Err(self.error(
1264                Span::splat(self.pos()),
1265                ast::ErrorKind::UnsupportedResharpRegex,
1266            )),
1267            hir::HirKind::Concat(body) => {
1268                let mut result = NodeId::EPS;
1269                for child in body {
1270                    let node = self.hir_to_node_id(child, tb)?;
1271                    result = tb.mk_concat(result, node);
1272                }
1273                Ok(result)
1274            }
1275            hir::HirKind::Alternation(_) => Err(self.error(
1276                Span::splat(self.pos()),
1277                ast::ErrorKind::UnsupportedResharpRegex,
1278            )),
1279        }
1280    }
1281
1282    fn translate_ast_to_hir(
1283        &mut self,
1284        orig_ast: &regex_syntax::ast::Ast,
1285        tb: &mut TB<'s>,
1286    ) -> Result<NodeId> {
1287        match self.translator.translate("", orig_ast) {
1288            Err(_) => Err(self.error(self.span(), ast::ErrorKind::UnicodeClassInvalid)),
1289            Ok(hir) => self.hir_to_node_id(&hir, tb),
1290        }
1291    }
1292
1293    fn translator_to_node_id(
1294        &mut self,
1295        orig_ast: &regex_syntax::ast::Ast,
1296        translator: &mut Option<Translator>,
1297        tb: &mut TB<'s>,
1298    ) -> Result<NodeId> {
1299        match translator {
1300            Some(tr) => {
1301                let hir = tr
1302                    .translate("", orig_ast)
1303                    .map_err(|e| self.unsupported_error(e))?;
1304                self.hir_to_node_id(&hir, tb)
1305            }
1306            None => self.translate_ast_to_hir(orig_ast, tb),
1307        }
1308    }
1309
1310    fn get_class(
1311        &mut self,
1312        negated: bool,
1313        kind: regex_syntax::ast::ClassPerlKind,
1314        tb: &mut TB<'s>,
1315    ) -> Result<NodeId> {
1316        let w = self
1317            .perl_classes
1318            .iter()
1319            .find(|(c_neg, c_kind, _)| *c_kind == kind && *c_neg == negated);
1320        match w {
1321            Some((_, _, value)) => Ok(*value),
1322            None => {
1323                let translated = if self.global_ascii_perl {
1324                    let pos = match kind {
1325                        regex_syntax::ast::ClassPerlKind::Word => {
1326                            let az = tb.mk_range_u8(b'a', b'z');
1327                            let big = tb.mk_range_u8(b'A', b'Z');
1328                            let dig = tb.mk_range_u8(b'0', b'9');
1329                            let us = tb.mk_u8(b'_');
1330                            tb.mk_unions([az, big, dig, us].into_iter())
1331                        }
1332                        regex_syntax::ast::ClassPerlKind::Digit => tb.mk_range_u8(b'0', b'9'),
1333                        regex_syntax::ast::ClassPerlKind::Space => {
1334                            let sp = tb.mk_u8(b' ');
1335                            let tab = tb.mk_u8(b'\t');
1336                            let nl = tb.mk_u8(b'\n');
1337                            let cr = tb.mk_u8(b'\r');
1338                            let ff = tb.mk_u8(0x0C);
1339                            let vt = tb.mk_u8(0x0B);
1340                            tb.mk_unions([sp, tab, nl, cr, ff, vt].into_iter())
1341                        }
1342                    };
1343                    if negated {
1344                        resharp_algebra::neg_class(tb, pos)
1345                    } else {
1346                        pos
1347                    }
1348                } else if self.global_unicode {
1349                    match kind {
1350                        regex_syntax::ast::ClassPerlKind::Word => {
1351                            if self.global_full_unicode {
1352                                self.unicode_classes.ensure_word_full(tb);
1353                            } else {
1354                                self.unicode_classes.ensure_word(tb);
1355                            }
1356                            if negated {
1357                                self.unicode_classes.non_word
1358                            } else {
1359                                self.unicode_classes.word
1360                            }
1361                        }
1362                        regex_syntax::ast::ClassPerlKind::Digit => {
1363                            if self.global_full_unicode {
1364                                self.unicode_classes.ensure_digit_full(tb);
1365                            } else {
1366                                self.unicode_classes.ensure_digit(tb);
1367                            }
1368                            if negated {
1369                                self.unicode_classes.non_digit
1370                            } else {
1371                                self.unicode_classes.digit
1372                            }
1373                        }
1374                        regex_syntax::ast::ClassPerlKind::Space => {
1375                            if self.global_full_unicode {
1376                                self.unicode_classes.ensure_space_full(tb);
1377                            } else {
1378                                self.unicode_classes.ensure_space(tb);
1379                            }
1380                            if negated {
1381                                self.unicode_classes.non_space
1382                            } else {
1383                                self.unicode_classes.space
1384                            }
1385                        }
1386                    }
1387                } else {
1388                    let pos = match kind {
1389                        regex_syntax::ast::ClassPerlKind::Word => {
1390                            let az = tb.mk_range_u8(b'a', b'z');
1391                            let big = tb.mk_range_u8(b'A', b'Z');
1392                            let dig = tb.mk_range_u8(b'0', b'9');
1393                            let us = tb.mk_u8(b'_');
1394                            tb.mk_unions([az, big, dig, us].into_iter())
1395                        }
1396                        regex_syntax::ast::ClassPerlKind::Digit => tb.mk_range_u8(b'0', b'9'),
1397                        regex_syntax::ast::ClassPerlKind::Space => {
1398                            let sp = tb.mk_u8(b' ');
1399                            let tab = tb.mk_u8(b'\t');
1400                            let nl = tb.mk_u8(b'\n');
1401                            let cr = tb.mk_u8(b'\r');
1402                            let ff = tb.mk_u8(0x0C);
1403                            let vt = tb.mk_u8(0x0B);
1404                            tb.mk_unions([sp, tab, nl, cr, ff, vt].into_iter())
1405                        }
1406                    };
1407                    if negated {
1408                        // negate within the byte, not the language, or `\D`/`\S`/`\W` wrongly match "".
1409                        resharp_algebra::neg_class(tb, pos)
1410                    } else {
1411                        pos
1412                    }
1413                };
1414                self.perl_classes.push((negated, kind, translated));
1415                Ok(translated)
1416            }
1417        }
1418    }
1419
1420    fn word_char_kind(ast: &Ast, left: bool) -> WordCharKind {
1421        use WordCharKind::*;
1422        match ast {
1423            Ast::Literal(lit) => {
1424                if is_word_byte(lit.c as u8) {
1425                    Word
1426                } else {
1427                    NonWord
1428                }
1429            }
1430            Ast::ClassPerl(c) => match (&c.kind, c.negated) {
1431                (&regex_syntax::ast::ClassPerlKind::Word, false) => Word,
1432                (&regex_syntax::ast::ClassPerlKind::Word, true) => NonWord,
1433                (&regex_syntax::ast::ClassPerlKind::Space, false) => NonWord,
1434                (&regex_syntax::ast::ClassPerlKind::Space, true) => Unknown,
1435                (&regex_syntax::ast::ClassPerlKind::Digit, false) => Word,
1436                (&regex_syntax::ast::ClassPerlKind::Digit, true) => Unknown,
1437            },
1438            Ast::ClassBracketed(c) => class_bracketed_word_kind(c),
1439            Ast::Dot(_) | Ast::Top(_) => Unknown,
1440            Ast::Group(g) => Self::word_char_kind(&g.ast, left),
1441            Ast::Concat(c) if !c.asts.is_empty() => {
1442                let dir: isize = if left { -1 } else { 1 };
1443                let edge = match Self::concat_edge_index(&c.asts, left) {
1444                    Some(e) => e,
1445                    None => return Unknown,
1446                };
1447                let kind = Self::word_char_kind(&c.asts[edge], left);
1448                match kind {
1449                    MaybeWord => {
1450                        match Self::concat_neighbor_kind(&c.asts, edge, dir) {
1451                            Word => Word,
1452                            Edge => MaybeWord,
1453                            _ => Unknown,
1454                        }
1455                    }
1456                    MaybeNonWord => {
1457                        match Self::concat_neighbor_kind(&c.asts, edge, dir) {
1458                            NonWord => NonWord,
1459                            Edge => MaybeNonWord,
1460                            _ => Unknown,
1461                        }
1462                    }
1463                    other => other,
1464                }
1465            }
1466            Ast::Alternation(alt) if !alt.asts.is_empty() => {
1467                let first = Self::word_char_kind(&alt.asts[0], left);
1468                if alt.asts[1..]
1469                    .iter()
1470                    .all(|a| Self::word_char_kind(a, left) == first)
1471                {
1472                    first
1473                } else {
1474                    Unknown
1475                }
1476            }
1477            Ast::Repetition(r) => {
1478                let inner = Self::word_char_kind(&r.ast, left);
1479                let nullable = matches!(
1480                    &r.op.kind,
1481                    ast::RepetitionKind::ZeroOrMore
1482                        | ast::RepetitionKind::ZeroOrOne
1483                        | ast::RepetitionKind::Range(
1484                            ast::RepetitionRange::Bounded(0, _)
1485                                | ast::RepetitionRange::Exactly(0)
1486                        )
1487                );
1488                if nullable {
1489                    match inner {
1490                        Word => MaybeWord,
1491                        NonWord => MaybeNonWord,
1492                        _ => Unknown,
1493                    }
1494                } else {
1495                    inner
1496                }
1497            }
1498            Ast::Lookaround(la) => match la.kind {
1499                ast::LookaroundKind::PositiveLookahead
1500                | ast::LookaroundKind::PositiveLookbehind => Self::word_char_kind(&la.ast, left),
1501                _ => Unknown,
1502            },
1503            Ast::Assertion(a) => match (&a.kind, left) {
1504                (ast::AssertionKind::EndText, false) => NonWord,
1505                (ast::AssertionKind::StartText, true) => NonWord,
1506                _ => Unknown,
1507            },
1508            _ => Unknown,
1509        }
1510    }
1511
1512    // ok to return None here, it's only an optimization
1513    fn edge_class_ast(ast: &Ast, left: bool) -> Option<&Ast> {
1514        match ast {
1515            Ast::Literal(_)
1516            | Ast::ClassPerl(_)
1517            | Ast::ClassBracketed(_)
1518            | Ast::ClassUnicode(_)
1519            | Ast::Dot(_)
1520            | Ast::Top(_) => Some(ast),
1521            Ast::Group(g) => Self::edge_class_ast(&g.ast, left),
1522            Ast::Concat(c) if !c.asts.is_empty() => {
1523                Self::concat_edge_index(&c.asts, left)
1524                    .and_then(|e| Self::edge_class_ast(&c.asts[e], left))
1525            }
1526            Ast::Repetition(r) => {
1527                let nullable = matches!(
1528                    &r.op.kind,
1529                    ast::RepetitionKind::ZeroOrMore
1530                        | ast::RepetitionKind::ZeroOrOne
1531                        | ast::RepetitionKind::Range(
1532                            ast::RepetitionRange::Bounded(0, _)
1533                                | ast::RepetitionRange::Exactly(0)
1534                        )
1535                );
1536                if nullable {
1537                    None
1538                } else {
1539                    Self::edge_class_ast(&r.ast, left)
1540                }
1541            }
1542            _ => None,
1543        }
1544    }
1545
1546    fn resolve_word_kind(
1547        &mut self,
1548        asts: &[Ast],
1549        idx: usize,
1550        dir: isize,
1551        translator: &mut Option<Translator>,
1552        tb: &mut TB<'s>,
1553        word_id: NodeId,
1554        not_word_id: NodeId,
1555    ) -> Result<WordCharKind> {
1556        use WordCharKind::*;
1557        let fast = Self::concat_neighbor_kind(asts, idx, dir);
1558        if fast != Unknown {
1559            return Ok(fast);
1560        }
1561        let neighbor_idx = (idx as isize + dir) as usize;
1562        let node = if let Some(edge) = Self::edge_class_ast(&asts[neighbor_idx], dir < 0) {
1563            self.ast_to_node_id(edge, translator, tb)?
1564        } else if dir > 0 {
1565            let mut bodies: Vec<NodeId> = vec![];
1566            let mut j = neighbor_idx;
1567            while j < asts.len() {
1568                match &asts[j] {
1569                    Ast::Lookaround(la) => {
1570                        let kind = la.kind.clone();
1571                        let lookbehind = matches!(
1572                            kind,
1573                            ast::LookaroundKind::PositiveLookbehind
1574                                | ast::LookaroundKind::NegativeLookbehind
1575                        );
1576                        if lookbehind {
1577                            j += 1;
1578                            continue;
1579                        }
1580                        let body = self.ast_to_node_id(&la.ast, translator, tb)?;
1581                        let body = tb.try_elim_lookarounds(body).ok_or_else(|| {
1582                            self.error(self.span(), ast::ErrorKind::UnsupportedResharpRegex)
1583                        })?;
1584                        let body_ts = tb.mk_concat(body, NodeId::TS);
1585                        let constraint = match kind {
1586                            ast::LookaroundKind::PositiveLookahead => body_ts,
1587                            ast::LookaroundKind::NegativeLookahead => tb.mk_compl(body_ts),
1588                            _ => unreachable!(),
1589                        };
1590                        bodies.push(constraint);
1591                        j += 1;
1592                    }
1593                    other => {
1594                        let n = self.ast_to_node_id(other, translator, tb)?;
1595                        let n = tb.try_elim_lookarounds(n).ok_or_else(|| {
1596                            self.error(self.span(), ast::ErrorKind::UnsupportedResharpRegex)
1597                        })?;
1598                        bodies.push(tb.mk_concat(n, NodeId::TS));
1599                        break;
1600                    }
1601                }
1602            }
1603            if bodies.is_empty() {
1604                return Ok(Unknown);
1605            }
1606            let combined = tb.mk_inters(bodies.into_iter());
1607            let word_prefix = tb.mk_concat(word_id, NodeId::TS);
1608            let non_word_prefix = tb.mk_concat(not_word_id, NodeId::TS);
1609            return if tb.subsumes(word_prefix, combined) == Some(true) {
1610                Ok(Word)
1611            } else if tb.subsumes(non_word_prefix, combined) == Some(true) {
1612                Ok(NonWord)
1613            } else {
1614                Ok(Unknown)
1615            };
1616        } else {
1617            let neighbor_node = self.ast_to_node_id(&asts[neighbor_idx], translator, tb)?;
1618            let neighbor_node = Self::strip_trailing_lookahead(tb, neighbor_node);
1619            let mut neighbor_node = tb
1620                .try_elim_lookarounds(neighbor_node)
1621                .ok_or_else(|| self.error(self.span(), ast::ErrorKind::UnsupportedResharpRegex))?;
1622            neighbor_node = tb.reverse(neighbor_node).or_else(|_| {
1623                Err(self.error(self.span(), ast::ErrorKind::UnsupportedResharpRegex))
1624            })?;
1625            let word_prefix = tb.mk_concat(word_id, NodeId::TS);
1626            let non_word_prefix = tb.mk_concat(not_word_id, NodeId::TS);
1627            return if tb.subsumes(word_prefix, neighbor_node) == Some(true) {
1628                Ok(Word)
1629            } else if tb.subsumes(non_word_prefix, neighbor_node) == Some(true) {
1630                Ok(NonWord)
1631            } else {
1632                Ok(Unknown)
1633            };
1634        };
1635        if tb.subsumes(word_id, node) == Some(true) {
1636            Ok(Word)
1637        } else if tb.subsumes(not_word_id, node) == Some(true) {
1638            Ok(NonWord)
1639        } else {
1640            Ok(Unknown)
1641        }
1642    }
1643
1644    fn strip_trailing_lookahead(tb: &mut TB<'s>, node: NodeId) -> NodeId {
1645        match tb.get_kind(node) {
1646            Kind::Lookahead if tb.get_min_max_length(node).1 == 0 => NodeId::EPS,
1647            Kind::Concat => {
1648                let l = node.left(tb);
1649                let r = node.right(tb);
1650                let stripped_r = Self::strip_trailing_lookahead(tb, r);
1651                if stripped_r == NodeId::EPS {
1652                    Self::strip_trailing_lookahead(tb, l)
1653                } else if stripped_r == r {
1654                    node
1655                } else {
1656                    tb.mk_concat(l, stripped_r)
1657                }
1658            }
1659            _ => node,
1660        }
1661    }
1662
1663    fn concat_edge_index(asts: &[Ast], left: bool) -> Option<usize> {
1664        let dir: isize = if left { -1 } else { 1 };
1665        let mut e = if left { asts.len() as isize - 1 } else { 0 };
1666        while e >= 0
1667            && (e as usize) < asts.len()
1668            && Self::is_transparent_for_dir(&asts[e as usize], dir)
1669        {
1670            e += dir;
1671        }
1672        if e < 0 || e as usize >= asts.len() {
1673            None
1674        } else {
1675            Some(e as usize)
1676        }
1677    }
1678
1679    fn is_transparent_for_dir(ast: &Ast, dir: isize) -> bool {
1680        match ast {
1681            Ast::Lookaround(la) => match la.kind {
1682                ast::LookaroundKind::PositiveLookahead | ast::LookaroundKind::NegativeLookahead => {
1683                    dir < 0
1684                }
1685                ast::LookaroundKind::PositiveLookbehind
1686                | ast::LookaroundKind::NegativeLookbehind => dir > 0,
1687            },
1688            Ast::Repetition(r) => matches!(
1689                &r.op.kind,
1690                ast::RepetitionKind::Range(ast::RepetitionRange::Exactly(0))
1691            ),
1692            _ => false,
1693        }
1694    }
1695
1696    fn concat_neighbor_kind(asts: &[Ast], idx: usize, dir: isize) -> WordCharKind {
1697        use WordCharKind::*;
1698        let next = idx as isize + dir;
1699        if next < 0 || next >= asts.len() as isize {
1700            return Edge;
1701        }
1702        if Self::is_transparent_for_dir(&asts[next as usize], dir) {
1703            return Self::concat_neighbor_kind(asts, next as usize, dir);
1704        }
1705        let kind = Self::word_char_kind(&asts[next as usize], dir < 0);
1706        match kind {
1707            MaybeWord => match Self::concat_neighbor_kind(asts, next as usize, dir) {
1708                Word => Word,
1709                _ => Unknown,
1710            },
1711            MaybeNonWord => match Self::concat_neighbor_kind(asts, next as usize, dir) {
1712                NonWord => NonWord,
1713                _ => Unknown,
1714            },
1715            other => other,
1716        }
1717    }
1718
1719    fn specialize_word_boundaries(
1720        &mut self,
1721        children: &mut [NodeId],
1722        tb: &mut TB<'s>,
1723    ) -> Result<()> {
1724        let wb = self.unicode_classes.wb;
1725        let non_wb = self.unicode_classes.non_wb;
1726        if wb == NodeId::MISSING {
1727            return Ok(());
1728        }
1729        let word = self.unicode_classes.word;
1730        let non_word = self.unicode_classes.non_word;
1731        if word == NodeId::MISSING {
1732            return Ok(());
1733        }
1734        // narrow check, could be done over the whole regex instead of one node
1735        // but i need to make sure the cost isnt too large
1736        let word_pref = tb.mk_concat(word, NodeId::TS);
1737        let non_word_pref = tb.mk_concat(non_word, NodeId::TS);
1738        let word_suf = tb.mk_concat(NodeId::TS, word);
1739        let non_word_suf = tb.mk_concat(NodeId::TS, non_word);
1740        let len = children.len();
1741        for k in 0..len {
1742            let l = if k == 0 {
1743                WordCharKind::Edge
1744            } else {
1745                use resharp_algebra::Kind;
1746                if tb.get_kind(children[k - 1]) == Kind::End
1747                    && (children[k] == wb || children[k] == non_wb)
1748                {
1749                    return Err(
1750                        self.error(self.span(), ast::ErrorKind::UnsupportedResharpRegex)
1751                    );
1752                }
1753                Self::classify(tb, children[k - 1], word_suf, non_word_suf)
1754            };
1755            let r = if k + 1 >= len {
1756                WordCharKind::Edge
1757            } else {
1758                Self::classify(tb, children[k + 1], word_pref, non_word_pref)
1759            };
1760            children[k] = Self::rewrite_wb_in_node(tb, children[k], wb, non_wb, word, l, r)
1761                .ok_or_else(|| self.error(self.span(), ast::ErrorKind::UnsupportedResharpRegex))?;
1762        }
1763        Ok(())
1764    }
1765
1766    fn rewrite_wb_in_node(
1767        b: &mut TB<'s>,
1768        node: NodeId,
1769        wb: NodeId,
1770        non_wb: NodeId,
1771        word: NodeId,
1772        left: WordCharKind,
1773        right: WordCharKind,
1774    ) -> Option<NodeId> {
1775        let boundary_match = if node == wb {
1776            true
1777        } else if node == non_wb {
1778            false
1779        } else if b.get_kind(node) == Kind::Union {
1780            let l = Self::rewrite_wb_in_node(b, node.left(b), wb, non_wb, word, left, right)?;
1781            let r = Self::rewrite_wb_in_node(b, node.right(b), wb, non_wb, word, left, right)?;
1782            return Some(b.mk_union(l, r));
1783        } else {
1784            return Some(node);
1785        };
1786        use WordCharKind::*;
1787        let result = match (left, right) {
1788            (NonWord, Word) | (Word, NonWord) => {
1789                if boundary_match {
1790                    NodeId::EPS
1791                } else {
1792                    NodeId::BOT
1793                }
1794            }
1795            (Word, Word) | (NonWord, NonWord) => {
1796                if boundary_match {
1797                    NodeId::BOT
1798                } else {
1799                    NodeId::EPS
1800                }
1801            }
1802            (Word, _) => {
1803                if boundary_match {
1804                    b.mk_neg_lookahead(word, 0)
1805                } else {
1806                    let tail = b.mk_concat(word, NodeId::TS);
1807                    b.mk_lookahead(tail, NodeId::MISSING, 0)
1808                }
1809            }
1810            (NonWord, _) => {
1811                if boundary_match {
1812                    let tail = b.mk_concat(word, NodeId::TS);
1813                    b.mk_lookahead(tail, NodeId::MISSING, 0)
1814                } else {
1815                    b.mk_neg_lookahead(word, 0)
1816                }
1817            }
1818            (_, Word) => {
1819                if boundary_match {
1820                    b.mk_neg_lookbehind(word)
1821                } else {
1822                    b.mk_lookbehind(word, NodeId::MISSING)
1823                }
1824            }
1825            (_, NonWord) => {
1826                if boundary_match {
1827                    b.mk_lookbehind(word, NodeId::MISSING)
1828                } else {
1829                    b.mk_neg_lookbehind(word)
1830                }
1831            }
1832            _ => return Some(node),
1833        };
1834        Some(result)
1835    }
1836
1837    fn classify(
1838        b: &mut TB<'s>,
1839        node: NodeId,
1840        word_dir: NodeId,
1841        non_word_dir: NodeId,
1842    ) -> WordCharKind {
1843        if b.contains_look(node) || b.contains_anchors(node) {
1844            return WordCharKind::Unknown;
1845        }
1846        if b.subsumes(word_dir, node) == Some(true) {
1847            WordCharKind::Word
1848        } else if b.subsumes(non_word_dir, node) == Some(true) {
1849            WordCharKind::NonWord
1850        } else {
1851            WordCharKind::Unknown
1852        }
1853    }
1854
1855    fn rewrite_word_boundary_in_concat(
1856        &mut self,
1857        asts: &[Ast],
1858        idx: usize,
1859        translator: &mut Option<Translator>,
1860        tb: &mut TB<'s>,
1861        negated: bool,
1862    ) -> Result<(NodeId, usize)> {
1863        use WordCharKind::*;
1864        if self.global_full_unicode {
1865            self.unicode_classes.ensure_word_full(tb);
1866        } else if self.global_unicode && !self.global_ascii_perl {
1867            self.unicode_classes.ensure_word(tb);
1868        } else {
1869            self.unicode_classes.ensure_word_ascii(tb);
1870        }
1871        let word_id = self.unicode_classes.word;
1872        let not_word_id = self.unicode_classes.non_word;
1873        let left = self.resolve_word_kind(asts, idx, -1, translator, tb, word_id, not_word_id)?;
1874        let right = self.resolve_word_kind(asts, idx, 1, translator, tb, word_id, not_word_id)?;
1875        let boundary_match = !negated;
1876        match (left, right) {
1877            (NonWord, Word) | (Word, NonWord) => Ok((
1878                if boundary_match {
1879                    NodeId::EPS
1880                } else {
1881                    NodeId::BOT
1882                },
1883                idx + 1,
1884            )),
1885            (Word, Word) | (NonWord, NonWord) => Ok((
1886                if boundary_match {
1887                    NodeId::BOT
1888                } else {
1889                    NodeId::EPS
1890                },
1891                idx + 1,
1892            )),
1893            (Word, _) => {
1894                if boundary_match {
1895                    Ok((tb.mk_neg_lookahead(word_id, 0), idx + 1))
1896                } else {
1897                    let tail = tb.mk_concat(word_id, NodeId::TS);
1898                    self.merge_boundary_with_following_lookaheads(asts, idx, tail, translator, tb)
1899                }
1900            }
1901            (NonWord, _) => {
1902                if boundary_match {
1903                    let tail = tb.mk_concat(word_id, NodeId::TS);
1904                    self.merge_boundary_with_following_lookaheads(asts, idx, tail, translator, tb)
1905                } else {
1906                    Ok((tb.mk_neg_lookahead(word_id, 0), idx + 1))
1907                }
1908            }
1909            (_, Word) => {
1910                if boundary_match {
1911                    Ok((tb.mk_neg_lookbehind(word_id), idx + 1))
1912                } else {
1913                    Ok((tb.mk_lookbehind(word_id, NodeId::MISSING), idx + 1))
1914                }
1915            }
1916            (_, NonWord) => {
1917                if boundary_match {
1918                    Ok((tb.mk_lookbehind(word_id, NodeId::MISSING), idx + 1))
1919                } else {
1920                    Ok((tb.mk_neg_lookbehind(word_id), idx + 1))
1921                }
1922            }
1923            //   \b = (?<=\w)(?!\w) | (?<!\w)(?=\w)
1924            //   \B = (?<=\w)(?=\w)  | (?<!\w)(?!\w)
1925            //   TODO: expensive and the cost could be reduced further
1926            _ => {
1927                self.unicode_classes.ensure_wb(tb);
1928                let node = if boundary_match {
1929                    self.unicode_classes.wb
1930                } else {
1931                    self.unicode_classes.non_wb
1932                };
1933                Ok((node, idx + 1))
1934            }
1935        }
1936    }
1937
1938    fn merge_boundary_with_following_lookaheads(
1939        &mut self,
1940        asts: &[Ast],
1941        wb_idx: usize,
1942        boundary_tail: NodeId,
1943        translator: &mut Option<Translator>,
1944        tb: &mut TB<'s>,
1945    ) -> Result<(NodeId, usize)> {
1946        let mut next = wb_idx + 1;
1947        let mut la_bodies = vec![boundary_tail];
1948        while next < asts.len() {
1949            match &asts[next] {
1950                Ast::Lookaround(la) if la.kind == ast::LookaroundKind::PositiveLookahead => {
1951                    let body = self.ast_to_node_id(&la.ast, translator, tb)?;
1952                    la_bodies.push(tb.mk_concat(body, NodeId::TS));
1953                    next += 1;
1954                }
1955                _ => break,
1956            }
1957        }
1958        let merged = tb.mk_inters(la_bodies.into_iter());
1959        Ok((tb.mk_lookahead(merged, NodeId::MISSING, 0), next))
1960    }
1961
1962    fn ast_to_node_id(
1963        &mut self,
1964        ast: &Ast,
1965        translator: &mut Option<Translator>,
1966        tb: &mut TB<'s>,
1967    ) -> Result<NodeId> {
1968        match ast {
1969            Ast::Empty(_) => Ok(NodeId::EPS),
1970            Ast::Flags(f) => {
1971                if f.flags.flag_state(ast::Flag::SwapGreed).is_some() {
1972                    return Err(self.error(f.span, ast::ErrorKind::UnsupportedResharpRegex));
1973                }
1974                let mut translator_builder = self.default_translator_builder();
1975                if let Some(state) = f.flags.flag_state(ast::Flag::CaseInsensitive) {
1976                    translator_builder.case_insensitive(state);
1977                }
1978                if let Some(state) = f.flags.flag_state(ast::Flag::Unicode) {
1979                    translator_builder.unicode(state);
1980                }
1981                if let Some(state) = f.flags.flag_state(ast::Flag::DotMatchesNewLine) {
1982                    self.dot_all.set(state);
1983                }
1984                if let Some(state) = f.flags.flag_state(ast::Flag::MultiLine) {
1985                    self.multiline.set(state);
1986                }
1987                let concat_translator = Some(translator_builder.build());
1988                *translator = concat_translator;
1989                Ok(NodeId::EPS)
1990            }
1991            Ast::Literal(l) => {
1992                let ast_lit = regex_syntax::ast::Ast::literal(*l.to_owned());
1993                self.translator_to_node_id(&ast_lit, translator, tb)
1994            }
1995            Ast::Top(_) => Ok(NodeId::TOP),
1996            Ast::Dot(_) => {
1997                let codepoint_dot = self.global_ascii_perl || self.global_full_unicode;
1998                let hirv = match (codepoint_dot, self.dot_all.get()) {
1999                    (true, true) => hir::Hir::dot(hir::Dot::AnyChar),
2000                    (true, false) => hir::Hir::dot(hir::Dot::AnyCharExceptLF),
2001                    (false, true) => return Ok(NodeId::TOP),
2002                    (false, false) => hir::Hir::dot(hir::Dot::AnyByteExceptLF),
2003                };
2004                self.hir_to_node_id(&hirv, tb)
2005            }
2006            Ast::Assertion(a) => match &a.kind {
2007                ast::AssertionKind::StartText => Ok(NodeId::BEGIN),
2008                ast::AssertionKind::EndText => Ok(NodeId::END),
2009                ast::AssertionKind::WordBoundary => {
2010                    let only = Ast::Assertion(a.clone());
2011                    let asts = std::slice::from_ref(&only);
2012                    let (node, _) =
2013                        self.rewrite_word_boundary_in_concat(asts, 0, translator, tb, false)?;
2014                    Ok(node)
2015                }
2016                ast::AssertionKind::NotWordBoundary => {
2017                    // bare \B with no surrounding concat: treat as singleton.
2018                    let only = Ast::Assertion(a.clone());
2019                    let asts = std::slice::from_ref(&only);
2020                    let (node, _) =
2021                        self.rewrite_word_boundary_in_concat(asts, 0, translator, tb, true)?;
2022                    Ok(node)
2023                }
2024                ast::AssertionKind::StartLine => {
2025                    if !self.multiline.get() {
2026                        return Ok(NodeId::BEGIN);
2027                    }
2028                    let left = NodeId::BEGIN;
2029                    let right = tb.mk_u8(b'\n');
2030                    let union = tb.mk_union(left, right);
2031                    Ok(tb.mk_lookbehind(union, NodeId::MISSING))
2032                }
2033                ast::AssertionKind::EndLine => {
2034                    if !self.multiline.get() {
2035                        return Ok(NodeId::END);
2036                    }
2037                    let left = NodeId::END;
2038                    let right = tb.mk_u8(b'\n');
2039                    let union = tb.mk_union(left, right);
2040                    Ok(tb.mk_lookahead(union, NodeId::MISSING, 0))
2041                }
2042                ast::AssertionKind::WordBoundaryStart => {
2043                    Err(self.error(a.span, ast::ErrorKind::UnsupportedResharpRegex))
2044                }
2045                ast::AssertionKind::WordBoundaryEnd => {
2046                    Err(self.error(a.span, ast::ErrorKind::UnsupportedResharpRegex))
2047                }
2048                ast::AssertionKind::WordBoundaryStartAngle => {
2049                    Err(self.error(a.span, ast::ErrorKind::UnsupportedResharpRegex))
2050                }
2051                ast::AssertionKind::WordBoundaryEndAngle => {
2052                    Err(self.error(a.span, ast::ErrorKind::UnsupportedResharpRegex))
2053                }
2054                ast::AssertionKind::WordBoundaryStartHalf => {
2055                    Err(self.error(a.span, ast::ErrorKind::UnsupportedResharpRegex))
2056                }
2057                ast::AssertionKind::WordBoundaryEndHalf => {
2058                    Err(self.error(a.span, ast::ErrorKind::UnsupportedResharpRegex))
2059                }
2060            },
2061            Ast::ClassUnicode(c) => {
2062                let tmp = regex_syntax::ast::ClassUnicode {
2063                    span: c.span,
2064                    negated: c.negated,
2065                    kind: c.kind.clone(),
2066                };
2067                if !c.negated {
2068                    if let regex_syntax::ast::ClassUnicodeKind::Named(s) = &c.kind {
2069                        match s.as_str() {
2070                            // \p{ascii} for ascii, \p{ascii}&\p{Letter} => [A-Za-z]
2071                            "ascii" => return Ok(tb.mk_range_u8(0, 127)),
2072                            // a single valid utf8 codepoint; \p{utf8}*&~(a) => non a, but valid utf8
2073                            "utf8" => return Ok(utf8_codepoint_node(tb)),
2074                            "hex" => {
2075                                let nums = tb.mk_range_u8(b'0', b'9');
2076                                let lets = tb.mk_range_u8(b'a', b'f');
2077                                let lets2 = tb.mk_range_u8(b'A', b'F');
2078                                let merged = tb.mk_unions([nums, lets, lets2].into_iter());
2079                                return Ok(merged);
2080                            }
2081                            _ => {}
2082                        }
2083                    };
2084                }
2085
2086                let orig_ast = regex_syntax::ast::Ast::class_unicode(tmp);
2087                self.translator_to_node_id(&orig_ast, translator, tb)
2088            }
2089            Ast::ClassPerl(c) => self.get_class(c.negated, c.kind.clone(), tb),
2090            Ast::ClassBracketed(c) => match &c.kind {
2091                regex_syntax::ast::ClassSet::Item(item) => {
2092                    if !c.negated && is_universal_perl_pair(item) {
2093                        if self.global_ascii_perl || self.global_full_unicode {
2094                            return Ok(self.any_codepoint_node(tb));
2095                        }
2096                        return Ok(NodeId::TOP);
2097                    }
2098                    if let regex_syntax::ast::ClassSetItem::Perl(p) = item {
2099                        return self.get_class(c.negated ^ p.negated, p.kind.clone(), tb);
2100                    }
2101                    let kind = if self.global_ascii_perl {
2102                        rewrite_ascii_perl_set(&c.kind)
2103                    } else {
2104                        c.kind.clone()
2105                    };
2106                    let tmp = regex_syntax::ast::ClassBracketed {
2107                        span: c.span,
2108                        negated: c.negated,
2109                        kind,
2110                    };
2111                    let orig_ast = regex_syntax::ast::Ast::class_bracketed(tmp);
2112                    self.translator_to_node_id(&orig_ast, translator, tb)
2113                }
2114                regex_syntax::ast::ClassSet::BinaryOp(_) => {
2115                    Err(self.error(c.span, ast::ErrorKind::UnsupportedResharpRegex))
2116                }
2117            },
2118            Ast::Repetition(r) => {
2119                let body = self.ast_to_node_id(&r.ast, translator, tb);
2120                match body {
2121                    Ok(body) => match &r.op.kind {
2122                        ast::RepetitionKind::ZeroOrOne => Ok(tb.mk_opt(body)),
2123                        ast::RepetitionKind::ZeroOrMore => Ok(tb.mk_star(body)),
2124                        ast::RepetitionKind::OneOrMore => Ok(tb.mk_plus(body)),
2125                        ast::RepetitionKind::Range(r) => match r {
2126                            ast::RepetitionRange::Exactly(n) => Ok(tb.mk_repeat(body, *n, *n)),
2127                            ast::RepetitionRange::AtLeast(n) => {
2128                                let rep = tb.mk_repeat(body, *n, *n);
2129                                let st = tb.mk_star(body);
2130                                Ok(tb.mk_concat(rep, st))
2131                            }
2132
2133                            ast::RepetitionRange::Bounded(n, m) => Ok(tb.mk_repeat(body, *n, *m)),
2134                        },
2135                    },
2136                    Err(_) => body,
2137                }
2138            }
2139            Ast::Lookaround(g) => {
2140                let body = self.ast_to_node_id(&g.ast, translator, tb)?;
2141                match g.kind {
2142                    ast::LookaroundKind::PositiveLookahead if body.contains_lookbehind(tb) => {
2143                        let mut prefix = NodeId::EPS;
2144                        let mut rest = body;
2145                        while tb.get_kind(rest) == Kind::Concat
2146                            && tb.get_kind(rest.left(tb)) == Kind::Lookbehind
2147                        {
2148                            prefix = tb.mk_concat(prefix, rest.left(tb));
2149                            rest = rest.right(tb);
2150                        }
2151                        if prefix == NodeId::EPS || rest.contains_lookbehind(tb) {
2152                            return Err(self.error(g.span, ast::ErrorKind::UnsupportedResharpRegex));
2153                        }
2154                        let la = tb.mk_lookahead(rest, NodeId::MISSING, 0);
2155                        Ok(tb.mk_concat(prefix, la))
2156                    }
2157                    ast::LookaroundKind::NegativeLookahead if body.contains_lookbehind(tb) => {
2158                        Err(self.error(g.span, ast::ErrorKind::UnsupportedResharpRegex))
2159                    }
2160                    ast::LookaroundKind::PositiveLookahead => {
2161                        Ok(tb.mk_lookahead(body, NodeId::MISSING, 0))
2162                    }
2163                    ast::LookaroundKind::PositiveLookbehind
2164                    | ast::LookaroundKind::NegativeLookbehind
2165                        if body.contains_lookahead(tb) =>
2166                    {
2167                        Err(self.error(g.span, ast::ErrorKind::UnsupportedResharpRegex))
2168                    }
2169                    ast::LookaroundKind::PositiveLookbehind => {
2170                        Ok(tb.mk_lookbehind(body, NodeId::MISSING))
2171                    }
2172                    ast::LookaroundKind::NegativeLookahead => Ok(tb.mk_neg_lookahead(body, 0)),
2173                    ast::LookaroundKind::NegativeLookbehind => Ok(tb.mk_neg_lookbehind(body)),
2174                }
2175            }
2176            Ast::Group(g) => {
2177                if let ast::GroupKind::NonCapturing(ref flags) = g.kind {
2178                    if !flags.items.is_empty() {
2179                        let mut translator_builder = self.default_translator_builder();
2180                        if let Some(state) = flags.flag_state(ast::Flag::CaseInsensitive) {
2181                            translator_builder.case_insensitive(state);
2182                        }
2183                        if let Some(state) = flags.flag_state(ast::Flag::Unicode) {
2184                            translator_builder.unicode(state);
2185                        }
2186                        let saved_dot_all = self.dot_all.get();
2187                        if let Some(state) = flags.flag_state(ast::Flag::DotMatchesNewLine) {
2188                            self.dot_all.set(state);
2189                        }
2190                        let saved_multiline = self.multiline.get();
2191                        if let Some(state) = flags.flag_state(ast::Flag::MultiLine) {
2192                            self.multiline.set(state);
2193                        }
2194                        let mut scoped = Some(translator_builder.build());
2195                        let result = self.ast_to_node_id(&g.ast, &mut scoped, tb);
2196                        self.dot_all.set(saved_dot_all);
2197                        self.multiline.set(saved_multiline);
2198                        return result;
2199                    }
2200                }
2201                self.ast_to_node_id(&g.ast, translator, tb)
2202            }
2203            Ast::Alternation(a) => {
2204                let mut children = vec![];
2205                for ast in &a.asts {
2206                    match self.ast_to_node_id(ast, translator, tb) {
2207                        Ok(node_id) => children.push(node_id),
2208                        Err(err) => return Err(err),
2209                    }
2210                }
2211                Ok(tb.mk_unions(children.iter().copied()))
2212            }
2213            Ast::Concat(c) => {
2214                let mut concat_translator: Option<Translator> = None;
2215                let mut children = vec![];
2216                let mut prev_boundary_child: Option<usize> = None;
2217                let mut i = 0;
2218                while i < c.asts.len() {
2219                    let ast = &c.asts[i];
2220                    match ast {
2221                        Ast::Flags(f) => {
2222                            if f.flags.flag_state(ast::Flag::SwapGreed).is_some() {
2223                                return Err(
2224                                    self.error(f.span, ast::ErrorKind::UnsupportedResharpRegex)
2225                                );
2226                            }
2227                            let mut translator_builder = self.default_translator_builder();
2228                            if let Some(state) = f.flags.flag_state(ast::Flag::CaseInsensitive) {
2229                                translator_builder.case_insensitive(state);
2230                            }
2231                            if let Some(state) = f.flags.flag_state(ast::Flag::Unicode) {
2232                                translator_builder.unicode(state);
2233                            }
2234                            if let Some(state) = f.flags.flag_state(ast::Flag::DotMatchesNewLine) {
2235                                self.dot_all.set(state);
2236                            }
2237                            if let Some(state) = f.flags.flag_state(ast::Flag::MultiLine) {
2238                                self.multiline.set(state);
2239                            }
2240                            concat_translator = Some(translator_builder.build());
2241                            *translator = concat_translator.clone();
2242                            i += 1;
2243                            continue;
2244                        }
2245                        Ast::Assertion(a)
2246                            if a.kind == ast::AssertionKind::WordBoundary
2247                                || a.kind == ast::AssertionKind::NotWordBoundary =>
2248                        {
2249                            let negated = a.kind == ast::AssertionKind::NotWordBoundary;
2250                            let node = self.rewrite_word_boundary_in_concat(
2251                                &c.asts, i, translator, tb, negated,
2252                            )?;
2253                            match prev_boundary_child {
2254                                Some(idx) => children[idx] = tb.mk_inter(children[idx], node.0),
2255                                None => {
2256                                    children.push(node.0);
2257                                    prev_boundary_child = Some(children.len() - 1);
2258                                }
2259                            }
2260                            i = node.1; // skip consumed lookaheads
2261                            continue;
2262                        }
2263                        _ => {}
2264                    }
2265                    match concat_translator {
2266                        Some(_) => match self.ast_to_node_id(ast, &mut concat_translator, tb) {
2267                            Ok(node_id) => {
2268                                if node_id != resharp_algebra::NodeId::EPS {
2269                                    prev_boundary_child = None;
2270                                    children.push(node_id);
2271                                }
2272                            }
2273                            Err(err) => return Err(err),
2274                        },
2275                        None => match self.ast_to_node_id(ast, translator, tb) {
2276                            Ok(node_id) => {
2277                                if node_id != resharp_algebra::NodeId::EPS {
2278                                    prev_boundary_child = None;
2279                                    children.push(node_id);
2280                                }
2281                            }
2282                            Err(err) => return Err(err),
2283                        },
2284                    }
2285                    i += 1;
2286                }
2287                self.specialize_word_boundaries(&mut children, tb)?;
2288                Ok(tb.mk_concats(children.iter().cloned()))
2289            }
2290            Ast::Intersection(intersection) => {
2291                let mut children = vec![];
2292                for ast in &intersection.asts {
2293                    match self.ast_to_node_id(ast, translator, tb) {
2294                        Ok(node_id) => children.push(node_id),
2295                        Err(err) => return Err(err),
2296                    }
2297                }
2298                Ok(tb.mk_inters(children.into_iter()))
2299            }
2300            Ast::Complement(complement) => {
2301                let body = self.ast_to_node_id(&complement.ast, translator, tb);
2302                body.map(|x| tb.mk_compl(x))
2303            }
2304        }
2305    }
2306
2307    fn parse_inner(&mut self) -> Result<Ast> {
2308        let mut concat = Concat {
2309            span: self.span(),
2310            asts: vec![],
2311        };
2312        loop {
2313            self.bump_space();
2314            if self.is_eof() {
2315                break;
2316            }
2317            match self.char() {
2318                '(' => concat = self.push_group(concat)?,
2319                ')' => concat = self.pop_group(concat)?,
2320                '|' => concat = self.push_alternate(concat)?,
2321                '&' => concat = self.push_intersect(concat)?,
2322                '~' => concat = self.push_compl_group(concat)?,
2323                '[' => {
2324                    let class = self.parse_set_class()?;
2325                    concat.asts.push(Ast::class_bracketed(class));
2326                }
2327                '?' => {
2328                    concat =
2329                        self.parse_uncounted_repetition(concat, ast::RepetitionKind::ZeroOrOne)?;
2330                }
2331                '*' => {
2332                    concat =
2333                        self.parse_uncounted_repetition(concat, ast::RepetitionKind::ZeroOrMore)?;
2334                }
2335                '+' => {
2336                    concat =
2337                        self.parse_uncounted_repetition(concat, ast::RepetitionKind::OneOrMore)?;
2338                }
2339                '{' => {
2340                    concat = self.parse_counted_repetition(concat)?;
2341                }
2342                _ => concat.asts.push(self.parse_primitive()?.into_ast()),
2343            }
2344            if self.stack_group.borrow().len() > self.max_depth {
2345                return Err(self.error(self.span(), ast::ErrorKind::UnsupportedResharpRegex));
2346            }
2347        }
2348        let ast = self.pop_group_end(concat)?;
2349        if expanded_ast_size(&ast, self.expanded_ast_limit) >= self.expanded_ast_limit
2350            || max_concat_length(&ast) >= self.max_list_len
2351        {
2352            return Err(self.error(*ast.span(), ast::ErrorKind::UnsupportedResharpRegex));
2353        }
2354        Ok(ast)
2355    }
2356
2357    fn parse(&mut self, tb: &mut TB<'s>) -> Result<NodeId> {
2358        let ast = self.parse_inner()?;
2359        if let Err(span) = ensure_lookbehind_at_start(&ast, true) {
2360            return Err(self.error(span, ast::ErrorKind::UnsupportedResharpRegex));
2361        }
2362        self.ast_to_node_id(&ast, &mut None, tb)
2363    }
2364
2365    #[inline(never)]
2366    fn parse_uncounted_repetition(
2367        &self,
2368        mut concat: ast::Concat,
2369        kind: ast::RepetitionKind,
2370    ) -> Result<ast::Concat> {
2371        // assert!(self.char() == '?' || self.char() == '*' || self.char() == '+');
2372        let op_start = self.pos();
2373        let ast = match concat.asts.pop() {
2374            Some(ast) => ast,
2375            None => return Err(self.error(self.span(), ast::ErrorKind::RepetitionMissing)),
2376        };
2377        match ast {
2378            Ast::Empty(_) | Ast::Flags(_) => {
2379                return Err(self.error(self.span(), ast::ErrorKind::RepetitionMissing))
2380            }
2381            _ => {}
2382        }
2383        if self.bump() && self.char() == '?' {
2384            return Err(self.error(
2385                Span::new(op_start, self.pos()),
2386                ast::ErrorKind::UnsupportedLazyQuantifier,
2387            ));
2388        }
2389        concat.asts.push(Ast::repetition(ast::Repetition {
2390            span: ast.span().with_end(self.pos()),
2391            op: ast::RepetitionOp {
2392                span: Span::new(op_start, self.pos()),
2393                kind,
2394            },
2395            greedy: true,
2396            ast: Box::new(ast),
2397        }));
2398        Ok(concat)
2399    }
2400
2401    #[inline(never)]
2402    fn parse_counted_repetition(&self, mut concat: ast::Concat) -> Result<ast::Concat> {
2403        assert!(self.char() == '{');
2404        let start = self.pos();
2405        let ast = match concat.asts.pop() {
2406            Some(ast) => ast,
2407            None => return Err(self.error(self.span(), ast::ErrorKind::RepetitionMissing)),
2408        };
2409        match ast {
2410            Ast::Empty(_) | Ast::Flags(_) => {
2411                return Err(self.error(self.span(), ast::ErrorKind::RepetitionMissing))
2412            }
2413            _ => {}
2414        }
2415        if !self.bump_and_bump_space() {
2416            return Err(self.error(
2417                Span::new(start, self.pos()),
2418                ast::ErrorKind::RepetitionCountUnclosed,
2419            ));
2420        }
2421        let count_start = specialize_err(
2422            self.parse_decimal(),
2423            ast::ErrorKind::DecimalEmpty,
2424            ast::ErrorKind::RepetitionCountDecimalEmpty,
2425        );
2426        if self.is_eof() {
2427            return Err(self.error(
2428                Span::new(start, self.pos()),
2429                ast::ErrorKind::RepetitionCountUnclosed,
2430            ));
2431        }
2432        let range = if self.char() == ',' {
2433            if !self.bump_and_bump_space() {
2434                return Err(self.error(
2435                    Span::new(start, self.pos()),
2436                    ast::ErrorKind::RepetitionCountUnclosed,
2437                ));
2438            }
2439            if self.char() != '}' {
2440                let count_start = match count_start {
2441                    Ok(c) => c,
2442                    Err(err) if err.kind == ast::ErrorKind::RepetitionCountDecimalEmpty => {
2443                        if self.parser().empty_min_range {
2444                            0
2445                        } else {
2446                            return Err(err);
2447                        }
2448                    }
2449                    err => err?,
2450                };
2451                let count_end = specialize_err(
2452                    self.parse_decimal(),
2453                    ast::ErrorKind::DecimalEmpty,
2454                    ast::ErrorKind::RepetitionCountDecimalEmpty,
2455                )?;
2456                ast::RepetitionRange::Bounded(count_start, count_end)
2457            } else {
2458                ast::RepetitionRange::AtLeast(count_start?)
2459            }
2460        } else {
2461            ast::RepetitionRange::Exactly(count_start?)
2462        };
2463
2464        if self.is_eof() || self.char() != '}' {
2465            return Err(self.error(
2466                Span::new(start, self.pos()),
2467                ast::ErrorKind::RepetitionCountUnclosed,
2468            ));
2469        }
2470
2471        if self.bump_and_bump_space() && self.char() == '?' {
2472            return Err(self.error(
2473                Span::new(start, self.pos()),
2474                ast::ErrorKind::UnsupportedLazyQuantifier,
2475            ));
2476        }
2477
2478        let op_span = Span::new(start, self.pos());
2479        if !range.is_valid() {
2480            return Err(self.error(op_span, ast::ErrorKind::RepetitionCountInvalid));
2481        }
2482
2483        let over_limit = match &range {
2484            ast::RepetitionRange::Exactly(n) => *n > self.max_repeat,
2485            ast::RepetitionRange::AtLeast(n) => *n > self.max_repeat,
2486            ast::RepetitionRange::Bounded(n, m) => *n > self.max_repeat || *m > self.max_repeat,
2487        };
2488        if over_limit {
2489            return Err(self.error(op_span, ast::ErrorKind::UnsupportedResharpRegex));
2490        }
2491        concat.asts.push(Ast::repetition(ast::Repetition {
2492            span: ast.span().with_end(self.pos()),
2493            op: ast::RepetitionOp {
2494                span: op_span,
2495                kind: ast::RepetitionKind::Range(range),
2496            },
2497            greedy: true,
2498            ast: Box::new(ast),
2499        }));
2500        Ok(concat)
2501    }
2502
2503    #[inline(never)]
2504    fn parse_group(&self) -> Result<Either<ast::SetFlags, ast::Group>> {
2505        assert_eq!(self.char(), '(');
2506        let open_span = self.span_char();
2507        self.bump();
2508        self.bump_space();
2509        if let Some((ahead, pos)) = self.is_lookaround_prefix() {
2510            let kind = match (pos, ahead) {
2511                (true, true) => LookaroundKind::PositiveLookahead,
2512                (true, false) => LookaroundKind::PositiveLookbehind,
2513                (false, true) => LookaroundKind::NegativeLookahead,
2514                (false, false) => LookaroundKind::NegativeLookbehind,
2515            };
2516            return Ok(Either::Right(ast::Group {
2517                span: open_span,
2518                kind: ast::GroupKind::Lookaround(kind),
2519                ast: Box::new(Ast::empty(self.span())),
2520            }));
2521        }
2522        let inner_span = self.span();
2523        let mut starts_with_p = true;
2524        if self.bump_if("?P<") || {
2525            starts_with_p = false;
2526            self.bump_if("?<")
2527        } {
2528            let capture_index = self.next_capture_index(open_span)?;
2529            let name = self.parse_capture_name(capture_index)?;
2530            Ok(Either::Right(ast::Group {
2531                span: open_span,
2532                kind: ast::GroupKind::CaptureName {
2533                    starts_with_p,
2534                    name,
2535                },
2536                ast: Box::new(Ast::empty(self.span())),
2537            }))
2538        } else if self.bump_if("?") {
2539            if self.is_eof() {
2540                return Err(self.error(open_span, ast::ErrorKind::GroupUnclosed));
2541            }
2542            let flags = self.parse_flags()?;
2543            let char_end = self.char();
2544            self.bump();
2545            if char_end == ')' {
2546                // We don't allow empty flags, e.g., `(?)`. We instead
2547                // interpret it as a repetition operator missing its argument.
2548                if flags.items.is_empty() {
2549                    return Err(self.error(inner_span, ast::ErrorKind::RepetitionMissing));
2550                }
2551                Ok(Either::Left(ast::SetFlags {
2552                    span: Span {
2553                        end: self.pos(),
2554                        ..open_span
2555                    },
2556                    flags,
2557                }))
2558            } else {
2559                assert_eq!(char_end, ':');
2560                Ok(Either::Right(ast::Group {
2561                    span: open_span,
2562                    kind: ast::GroupKind::NonCapturing(flags),
2563                    ast: Box::new(Ast::empty(self.span())),
2564                }))
2565            }
2566        } else {
2567            let capture_index = self.next_capture_index(open_span)?;
2568            Ok(Either::Right(ast::Group {
2569                span: open_span,
2570                kind: ast::GroupKind::CaptureIndex(capture_index),
2571                ast: Box::new(Ast::empty(self.span())),
2572            }))
2573        }
2574    }
2575
2576    #[inline(never)]
2577    fn parse_capture_name(&self, capture_index: u32) -> Result<ast::CaptureName> {
2578        if self.is_eof() {
2579            return Err(self.error(self.span(), ast::ErrorKind::GroupNameUnexpectedEof));
2580        }
2581        let start = self.pos();
2582        loop {
2583            if self.char() == '>' {
2584                break;
2585            }
2586            if !is_capture_char(self.char(), self.pos() == start) {
2587                return Err(self.error(self.span_char(), ast::ErrorKind::GroupNameInvalid));
2588            }
2589            if !self.bump() {
2590                break;
2591            }
2592        }
2593        let end = self.pos();
2594        if self.is_eof() {
2595            return Err(self.error(self.span(), ast::ErrorKind::GroupNameUnexpectedEof));
2596        }
2597        assert_eq!(self.char(), '>');
2598        self.bump();
2599        let name = &self.pattern()[start.offset..end.offset];
2600        if name.is_empty() {
2601            return Err(self.error(Span::new(start, start), ast::ErrorKind::GroupNameEmpty));
2602        }
2603        let capname = ast::CaptureName {
2604            span: Span::new(start, end),
2605            name: name.to_string(),
2606            index: capture_index,
2607        };
2608        self.add_capture_name(&capname)?;
2609        Ok(capname)
2610    }
2611
2612    #[inline(never)]
2613    fn parse_flags(&self) -> Result<ast::Flags> {
2614        let mut flags = ast::Flags {
2615            span: self.span(),
2616            items: vec![],
2617        };
2618        let mut last_was_negation = None;
2619        while self.char() != ':' && self.char() != ')' {
2620            if self.char() == '-' {
2621                last_was_negation = Some(self.span_char());
2622                let item = ast::FlagsItem {
2623                    span: self.span_char(),
2624                    kind: ast::FlagsItemKind::Negation,
2625                };
2626                if let Some(i) = flags.add_item(item) {
2627                    return Err(self.error(
2628                        self.span_char(),
2629                        ast::ErrorKind::FlagRepeatedNegation {
2630                            original: flags.items[i].span,
2631                        },
2632                    ));
2633                }
2634            } else {
2635                last_was_negation = None;
2636                let item = ast::FlagsItem {
2637                    span: self.span_char(),
2638                    kind: ast::FlagsItemKind::Flag(self.parse_flag()?),
2639                };
2640                if let Some(i) = flags.add_item(item) {
2641                    return Err(self.error(
2642                        self.span_char(),
2643                        ast::ErrorKind::FlagDuplicate {
2644                            original: flags.items[i].span,
2645                        },
2646                    ));
2647                }
2648            }
2649            if !self.bump() {
2650                return Err(self.error(self.span(), ast::ErrorKind::FlagUnexpectedEof));
2651            }
2652        }
2653        if let Some(span) = last_was_negation {
2654            return Err(self.error(span, ast::ErrorKind::FlagDanglingNegation));
2655        }
2656        flags.span.end = self.pos();
2657        Ok(flags)
2658    }
2659
2660    #[inline(never)]
2661    fn parse_flag(&self) -> Result<ast::Flag> {
2662        match self.char() {
2663            'i' => Ok(ast::Flag::CaseInsensitive),
2664            'm' => Ok(ast::Flag::MultiLine),
2665            's' => Ok(ast::Flag::DotMatchesNewLine),
2666            'U' => Ok(ast::Flag::SwapGreed),
2667            'u' => Ok(ast::Flag::Unicode),
2668            'R' => Ok(ast::Flag::CRLF),
2669            'x' => Ok(ast::Flag::IgnoreWhitespace),
2670            _ => Err(self.error(self.span_char(), ast::ErrorKind::FlagUnrecognized)),
2671        }
2672    }
2673
2674    fn parse_primitive(&self) -> Result<Primitive> {
2675        match self.char() {
2676            '\\' => self.parse_escape(),
2677            '_' => {
2678                let ast = Primitive::Top(self.span_char());
2679                self.bump();
2680                Ok(ast)
2681            }
2682            '.' => {
2683                let ast = Primitive::Dot(self.span_char());
2684                self.bump();
2685                Ok(ast)
2686            }
2687            '^' => {
2688                let ast = Primitive::Assertion(ast::Assertion {
2689                    span: self.span_char(),
2690                    kind: ast::AssertionKind::StartLine,
2691                });
2692                self.bump();
2693                Ok(ast)
2694            }
2695            '$' => {
2696                let ast = Primitive::Assertion(ast::Assertion {
2697                    span: self.span_char(),
2698                    kind: ast::AssertionKind::EndLine,
2699                });
2700                self.bump();
2701                Ok(ast)
2702            }
2703            c => {
2704                let ast = Primitive::Literal(Literal {
2705                    span: self.span_char(),
2706                    kind: LiteralKind::Verbatim,
2707                    c,
2708                });
2709                self.bump();
2710                Ok(ast)
2711            }
2712        }
2713    }
2714
2715    #[inline(never)]
2716    fn parse_escape(&self) -> Result<Primitive> {
2717        assert_eq!(self.char(), '\\');
2718        let start = self.pos();
2719        if !self.bump() {
2720            return Err(self.error(
2721                Span::new(start, self.pos()),
2722                ast::ErrorKind::EscapeUnexpectedEof,
2723            ));
2724        }
2725        let c = self.char();
2726        // Put some of the more complicated routines into helpers.
2727        match c {
2728            '0'..='9' => {
2729                if !self.parser().octal {
2730                    return Err(self.error(
2731                        Span::new(start, self.span_char().end),
2732                        ast::ErrorKind::UnsupportedBackreference,
2733                    ));
2734                }
2735                let mut lit = self.parse_octal();
2736                lit.span.start = start;
2737                return Ok(Primitive::Literal(lit));
2738            }
2739            'x' | 'u' | 'U' => {
2740                let mut lit = self.parse_hex()?;
2741                lit.span.start = start;
2742                return Ok(Primitive::Literal(lit));
2743            }
2744            'p' | 'P' => {
2745                let mut cls = self.parse_unicode_class()?;
2746                cls.span.start = start;
2747                return Ok(Primitive::Unicode(cls));
2748            }
2749            'd' | 's' | 'w' | 'D' | 'S' | 'W' => {
2750                let mut cls = self.parse_perl_class();
2751                cls.span.start = start;
2752                return Ok(Primitive::Perl(cls));
2753            }
2754            _ => {}
2755        }
2756
2757        // Handle all of the one letter sequences inline.
2758        self.bump();
2759        let span = Span::new(start, self.pos());
2760        if is_meta_character(c) {
2761            return Ok(Primitive::Literal(Literal {
2762                span,
2763                kind: LiteralKind::Meta,
2764                c,
2765            }));
2766        }
2767        if is_escapeable_character(c) {
2768            return Ok(Primitive::Literal(Literal {
2769                span,
2770                kind: LiteralKind::Superfluous,
2771                c,
2772            }));
2773        }
2774        let special = |kind, c| {
2775            Ok(Primitive::Literal(Literal {
2776                span,
2777                kind: LiteralKind::Special(kind),
2778                c,
2779            }))
2780        };
2781        match c {
2782            'a' => special(SpecialLiteralKind::Bell, '\x07'),
2783            'f' => special(SpecialLiteralKind::FormFeed, '\x0C'),
2784            't' => special(SpecialLiteralKind::Tab, '\t'),
2785            'n' => special(SpecialLiteralKind::LineFeed, '\n'),
2786            'r' => special(SpecialLiteralKind::CarriageReturn, '\r'),
2787            'v' => special(SpecialLiteralKind::VerticalTab, '\x0B'),
2788            'A' => Ok(Primitive::Assertion(ast::Assertion {
2789                span,
2790                kind: ast::AssertionKind::StartText,
2791            })),
2792            'z' => Ok(Primitive::Assertion(ast::Assertion {
2793                span,
2794                kind: ast::AssertionKind::EndText,
2795            })),
2796            'b' => {
2797                let mut wb = ast::Assertion {
2798                    span,
2799                    kind: ast::AssertionKind::WordBoundary,
2800                };
2801                // After a \b, we "try" to parse things like \b{start} for
2802                // special word boundary assertions.
2803                if !self.is_eof() && self.char() == '{' {
2804                    if let Some(kind) = self.maybe_parse_special_word_boundary(start)? {
2805                        wb.kind = kind;
2806                        wb.span.end = self.pos();
2807                    }
2808                }
2809                Ok(Primitive::Assertion(wb))
2810            }
2811            'B' => Ok(Primitive::Assertion(ast::Assertion {
2812                span,
2813                kind: ast::AssertionKind::NotWordBoundary,
2814            })),
2815            '<' => Ok(Primitive::Assertion(ast::Assertion {
2816                span,
2817                kind: ast::AssertionKind::WordBoundaryStartAngle,
2818            })),
2819            '>' => Ok(Primitive::Assertion(ast::Assertion {
2820                span,
2821                kind: ast::AssertionKind::WordBoundaryEndAngle,
2822            })),
2823            _ => Err(self.error(span, ast::ErrorKind::EscapeUnrecognized)),
2824        }
2825    }
2826
2827    fn maybe_parse_special_word_boundary(
2828        &self,
2829        wb_start: Position,
2830    ) -> Result<Option<ast::AssertionKind>> {
2831        assert_eq!(self.char(), '{');
2832
2833        let is_valid_char = |c| matches!(c, 'A'..='Z' | 'a'..='z' | '-');
2834        let start = self.pos();
2835        if !self.bump_and_bump_space() {
2836            return Err(self.error(
2837                Span::new(wb_start, self.pos()),
2838                ast::ErrorKind::SpecialWordOrRepetitionUnexpectedEof,
2839            ));
2840        }
2841        let start_contents = self.pos();
2842        if !is_valid_char(self.char()) {
2843            self.parser().pos.set(start);
2844            return Ok(None);
2845        }
2846
2847        // Now collect up our chars until we see a '}'.
2848        let mut scratch = self.parser().scratch.borrow_mut();
2849        scratch.clear();
2850        while !self.is_eof() && is_valid_char(self.char()) {
2851            scratch.push(self.char());
2852            self.bump_and_bump_space();
2853        }
2854        if self.is_eof() || self.char() != '}' {
2855            return Err(self.error(
2856                Span::new(start, self.pos()),
2857                ast::ErrorKind::SpecialWordBoundaryUnclosed,
2858            ));
2859        }
2860        let end = self.pos();
2861        self.bump();
2862        let kind = match scratch.as_str() {
2863            "start" => ast::AssertionKind::WordBoundaryStart,
2864            "end" => ast::AssertionKind::WordBoundaryEnd,
2865            "start-half" => ast::AssertionKind::WordBoundaryStartHalf,
2866            "end-half" => ast::AssertionKind::WordBoundaryEndHalf,
2867            _ => {
2868                return Err(self.error(
2869                    Span::new(start_contents, end),
2870                    ast::ErrorKind::SpecialWordBoundaryUnrecognized,
2871                ))
2872            }
2873        };
2874        Ok(Some(kind))
2875    }
2876
2877    #[inline(never)]
2878    fn parse_octal(&self) -> Literal {
2879        assert!(self.parser().octal);
2880        assert!('0' <= self.char() && self.char() <= '7');
2881        let start = self.pos();
2882        // Parse up to two more digits.
2883        while self.bump()
2884            && '0' <= self.char()
2885            && self.char() <= '7'
2886            && self.pos().offset - start.offset <= 2
2887        {}
2888        let end = self.pos();
2889        let octal = &self.pattern()[start.offset..end.offset];
2890        // Parsing the octal should never fail since the above guarantees a
2891        // valid number.
2892        let codepoint = u32::from_str_radix(octal, 8).expect("valid octal number");
2893        // The max value for 3 digit octal is 0777 = 511 and [0, 511] has no
2894        // invalid Unicode scalar values.
2895        let c = char::from_u32(codepoint).expect("Unicode scalar value");
2896        Literal {
2897            span: Span::new(start, end),
2898            kind: LiteralKind::Octal,
2899            c,
2900        }
2901    }
2902
2903    #[inline(never)]
2904    fn parse_hex(&self) -> Result<Literal> {
2905        assert!(self.char() == 'x' || self.char() == 'u' || self.char() == 'U');
2906
2907        let hex_kind = match self.char() {
2908            'x' => HexLiteralKind::X,
2909            'u' => HexLiteralKind::UnicodeShort,
2910            _ => HexLiteralKind::UnicodeLong,
2911        };
2912        if !self.bump_and_bump_space() {
2913            return Err(self.error(self.span(), ast::ErrorKind::EscapeUnexpectedEof));
2914        }
2915        if self.char() == '{' {
2916            self.parse_hex_brace(hex_kind)
2917        } else {
2918            self.parse_hex_digits(hex_kind)
2919        }
2920    }
2921
2922    #[inline(never)]
2923    fn parse_hex_digits(&self, kind: HexLiteralKind) -> Result<Literal> {
2924        let mut scratch = self.parser().scratch.borrow_mut();
2925        scratch.clear();
2926
2927        let start = self.pos();
2928        for i in 0..kind.digits() {
2929            if i > 0 && !self.bump_and_bump_space() {
2930                return Err(self.error(self.span(), ast::ErrorKind::EscapeUnexpectedEof));
2931            }
2932            if !is_hex(self.char()) {
2933                return Err(self.error(self.span_char(), ast::ErrorKind::EscapeHexInvalidDigit));
2934            }
2935            scratch.push(self.char());
2936        }
2937        self.bump_and_bump_space();
2938        let end = self.pos();
2939        let hex = scratch.as_str();
2940        match u32::from_str_radix(hex, 16).ok().and_then(char::from_u32) {
2941            None => Err(self.error(Span::new(start, end), ast::ErrorKind::EscapeHexInvalid)),
2942            Some(c) => Ok(Literal {
2943                span: Span::new(start, end),
2944                kind: LiteralKind::HexFixed(kind),
2945                c,
2946            }),
2947        }
2948    }
2949
2950    #[inline(never)]
2951    fn parse_hex_brace(&self, kind: HexLiteralKind) -> Result<Literal> {
2952        let mut scratch = self.parser().scratch.borrow_mut();
2953        scratch.clear();
2954
2955        let brace_pos = self.pos();
2956        let start = self.span_char().end;
2957        while self.bump_and_bump_space() && self.char() != '}' {
2958            if !is_hex(self.char()) {
2959                return Err(self.error(self.span_char(), ast::ErrorKind::EscapeHexInvalidDigit));
2960            }
2961            scratch.push(self.char());
2962        }
2963        if self.is_eof() {
2964            return Err(self.error(
2965                Span::new(brace_pos, self.pos()),
2966                ast::ErrorKind::EscapeUnexpectedEof,
2967            ));
2968        }
2969        let end = self.pos();
2970        let hex = scratch.as_str();
2971        assert_eq!(self.char(), '}');
2972        self.bump_and_bump_space();
2973
2974        if hex.is_empty() {
2975            return Err(self.error(
2976                Span::new(brace_pos, self.pos()),
2977                ast::ErrorKind::EscapeHexEmpty,
2978            ));
2979        }
2980        match u32::from_str_radix(hex, 16).ok().and_then(char::from_u32) {
2981            None => Err(self.error(Span::new(start, end), ast::ErrorKind::EscapeHexInvalid)),
2982            Some(c) => Ok(Literal {
2983                span: Span::new(start, self.pos()),
2984                kind: LiteralKind::HexBrace(kind),
2985                c,
2986            }),
2987        }
2988    }
2989
2990    fn parse_decimal(&self) -> Result<u32> {
2991        let mut scratch = self.parser().scratch.borrow_mut();
2992        scratch.clear();
2993
2994        while !self.is_eof() && self.char().is_whitespace() {
2995            self.bump();
2996        }
2997        let start = self.pos();
2998        while !self.is_eof() && '0' <= self.char() && self.char() <= '9' {
2999            scratch.push(self.char());
3000            self.bump_and_bump_space();
3001        }
3002        let span = Span::new(start, self.pos());
3003        while !self.is_eof() && self.char().is_whitespace() {
3004            self.bump_and_bump_space();
3005        }
3006        let digits = scratch.as_str();
3007        if digits.is_empty() {
3008            return Err(self.error(span, ast::ErrorKind::DecimalEmpty));
3009        }
3010        match digits.parse::<u32>().ok() {
3011            Some(n) => Ok(n),
3012            None => Err(self.error(span, ast::ErrorKind::DecimalInvalid)),
3013        }
3014    }
3015
3016    #[inline(never)]
3017    fn parse_set_class(&self) -> Result<ClassBracketed> {
3018        assert_eq!(self.char(), '[');
3019
3020        let mut union = ClassSetUnion {
3021            span: self.span(),
3022            items: vec![],
3023        };
3024        loop {
3025            self.bump_space();
3026            if self.is_eof() {
3027                return Err(self.unclosed_class_error());
3028            }
3029            match self.char() {
3030                '[' => {
3031                    if !self.parser().stack_class.borrow().is_empty() {
3032                        if let Some(cls) = self.maybe_parse_ascii_class() {
3033                            union.push(ClassSetItem::Ascii(cls));
3034                            continue;
3035                        }
3036                    }
3037                    union = self.push_class_open(union)?;
3038                }
3039                ']' => match self.pop_class(union)? {
3040                    Either::Left(nested_union) => {
3041                        union = nested_union;
3042                    }
3043                    Either::Right(class) => return Ok(class),
3044                },
3045                '&' if self.peek() == Some('&') => {
3046                    assert!(self.bump_if("&&"));
3047                    union = self.push_class_op(ClassSetBinaryOpKind::Intersection, union);
3048                }
3049                '-' if self.peek() == Some('-') => {
3050                    assert!(self.bump_if("--"));
3051                    union = self.push_class_op(ClassSetBinaryOpKind::Difference, union);
3052                }
3053                '~' if self.peek() == Some('~') => {
3054                    assert!(self.bump_if("~~"));
3055                    union = self.push_class_op(ClassSetBinaryOpKind::SymmetricDifference, union);
3056                }
3057                _ => {
3058                    union.push(self.parse_set_class_range()?);
3059                }
3060            }
3061        }
3062    }
3063
3064    #[inline(never)]
3065    fn parse_set_class_range(&self) -> Result<ClassSetItem> {
3066        let prim1 = self.parse_set_class_item()?;
3067        self.bump_space();
3068        if self.is_eof() {
3069            return Err(self.unclosed_class_error());
3070        }
3071        if self.char() != '-' || self.peek_space() == Some(']') || self.peek_space() == Some('-') {
3072            return prim1.into_class_set_item(self);
3073        }
3074        if !self.bump_and_bump_space() {
3075            return Err(self.unclosed_class_error());
3076        }
3077        let prim2 = self.parse_set_class_item()?;
3078        let range = ClassSetRange {
3079            span: Span::new(prim1.span().start, prim2.span().end),
3080            start: prim1.into_class_literal(self)?,
3081            end: prim2.into_class_literal(self)?,
3082        };
3083        if !range.is_valid() {
3084            return Err(self.error(range.span, ast::ErrorKind::ClassRangeInvalid));
3085        }
3086        Ok(ClassSetItem::Range(range))
3087    }
3088
3089    #[inline(never)]
3090    fn parse_set_class_item(&self) -> Result<Primitive> {
3091        if self.char() == '\\' {
3092            self.parse_escape()
3093        } else {
3094            let x = Primitive::Literal(Literal {
3095                span: self.span_char(),
3096                kind: LiteralKind::Verbatim,
3097                c: self.char(),
3098            });
3099            self.bump();
3100            Ok(x)
3101        }
3102    }
3103
3104    #[inline(never)]
3105    fn parse_set_class_open(&self) -> Result<(ClassBracketed, ClassSetUnion)> {
3106        assert_eq!(self.char(), '[');
3107        let start = self.pos();
3108        if !self.bump_and_bump_space() {
3109            return Err(self.error(Span::new(start, self.pos()), ast::ErrorKind::ClassUnclosed));
3110        }
3111
3112        let negated = if self.char() != '^' {
3113            false
3114        } else {
3115            if !self.bump_and_bump_space() {
3116                return Err(self.error(Span::new(start, self.pos()), ast::ErrorKind::ClassUnclosed));
3117            }
3118            true
3119        };
3120        // Accept any number of `-` as literal `-`.
3121        let mut union = ClassSetUnion {
3122            span: self.span(),
3123            items: vec![],
3124        };
3125        while self.char() == '-' {
3126            union.push(ClassSetItem::Literal(Literal {
3127                span: self.span_char(),
3128                kind: LiteralKind::Verbatim,
3129                c: '-',
3130            }));
3131            if !self.bump_and_bump_space() {
3132                return Err(self.error(Span::new(start, start), ast::ErrorKind::ClassUnclosed));
3133            }
3134        }
3135        // If `]` is the *first* char in a set, then interpret it as a literal
3136        // `]`. That is, an empty class is impossible to write.
3137        if union.items.is_empty() && self.char() == ']' {
3138            union.push(ClassSetItem::Literal(Literal {
3139                span: self.span_char(),
3140                kind: LiteralKind::Verbatim,
3141                c: ']',
3142            }));
3143            if !self.bump_and_bump_space() {
3144                return Err(self.error(Span::new(start, self.pos()), ast::ErrorKind::ClassUnclosed));
3145            }
3146        }
3147        let set = ClassBracketed {
3148            span: Span::new(start, self.pos()),
3149            negated,
3150            kind: ClassSet::union(ClassSetUnion {
3151                span: Span::new(union.span.start, union.span.start),
3152                items: vec![],
3153            }),
3154        };
3155        Ok((set, union))
3156    }
3157
3158    #[inline(never)]
3159    fn maybe_parse_ascii_class(&self) -> Option<ClassAscii> {
3160        assert_eq!(self.char(), '[');
3161        // If parsing fails, then we back up the parser to this starting point.
3162        let start = self.pos();
3163        let mut negated = false;
3164        if !self.bump() || self.char() != ':' {
3165            self.parser().pos.set(start);
3166            return None;
3167        }
3168        if !self.bump() {
3169            self.parser().pos.set(start);
3170            return None;
3171        }
3172        if self.char() == '^' {
3173            negated = true;
3174            if !self.bump() {
3175                self.parser().pos.set(start);
3176                return None;
3177            }
3178        }
3179        let name_start = self.offset();
3180        while self.char() != ':' && self.bump() {}
3181        if self.is_eof() {
3182            self.parser().pos.set(start);
3183            return None;
3184        }
3185        let name = &self.pattern()[name_start..self.offset()];
3186        if !self.bump_if(":]") {
3187            self.parser().pos.set(start);
3188            return None;
3189        }
3190        let kind = match regex_syntax::ast::ClassAsciiKind::from_name(name) {
3191            Some(kind) => kind,
3192            None => {
3193                self.parser().pos.set(start);
3194                return None;
3195            }
3196        };
3197        Some(ClassAscii {
3198            span: Span::new(start, self.pos()),
3199            kind,
3200            negated,
3201        })
3202    }
3203
3204    #[inline(never)]
3205    fn parse_unicode_class(&self) -> Result<ClassUnicode> {
3206        assert!(self.char() == 'p' || self.char() == 'P');
3207
3208        let mut scratch = self.parser().scratch.borrow_mut();
3209        scratch.clear();
3210
3211        let negated = self.char() == 'P';
3212        if !self.bump_and_bump_space() {
3213            return Err(self.error(self.span(), ast::ErrorKind::EscapeUnexpectedEof));
3214        }
3215        let (start, kind) = if self.char() == '{' {
3216            let start = self.span_char().end;
3217            while self.bump_and_bump_space() && self.char() != '}' {
3218                scratch.push(self.char());
3219            }
3220            if self.is_eof() {
3221                return Err(self.error(self.span(), ast::ErrorKind::EscapeUnexpectedEof));
3222            }
3223            assert_eq!(self.char(), '}');
3224            self.bump();
3225
3226            let name = scratch.as_str();
3227            if let Some(i) = name.find("!=") {
3228                (
3229                    start,
3230                    ClassUnicodeKind::NamedValue {
3231                        op: ClassUnicodeOpKind::NotEqual,
3232                        name: name[..i].to_string(),
3233                        value: name[i + 2..].to_string(),
3234                    },
3235                )
3236            } else if let Some(i) = name.find(':') {
3237                (
3238                    start,
3239                    ClassUnicodeKind::NamedValue {
3240                        op: ClassUnicodeOpKind::Colon,
3241                        name: name[..i].to_string(),
3242                        value: name[i + 1..].to_string(),
3243                    },
3244                )
3245            } else if let Some(i) = name.find('=') {
3246                (
3247                    start,
3248                    ClassUnicodeKind::NamedValue {
3249                        op: ClassUnicodeOpKind::Equal,
3250                        name: name[..i].to_string(),
3251                        value: name[i + 1..].to_string(),
3252                    },
3253                )
3254            } else {
3255                (start, ClassUnicodeKind::Named(name.to_string()))
3256            }
3257        } else {
3258            let start = self.pos();
3259            let c = self.char();
3260            if c == '\\' {
3261                return Err(self.error(self.span_char(), ast::ErrorKind::UnicodeClassInvalid));
3262            }
3263            self.bump_and_bump_space();
3264            let kind = ClassUnicodeKind::OneLetter(c);
3265            (start, kind)
3266        };
3267        Ok(ClassUnicode {
3268            span: Span::new(start, self.pos()),
3269            negated,
3270            kind,
3271        })
3272    }
3273
3274    #[inline(never)]
3275    fn parse_perl_class(&self) -> ClassPerl {
3276        let c = self.char();
3277        let span = self.span_char();
3278        self.bump();
3279        let (negated, kind) = match c {
3280            'd' => (false, regex_syntax::ast::ClassPerlKind::Digit),
3281            'D' => (true, regex_syntax::ast::ClassPerlKind::Digit),
3282            's' => (false, regex_syntax::ast::ClassPerlKind::Space),
3283            'S' => (true, regex_syntax::ast::ClassPerlKind::Space),
3284            'w' => (false, regex_syntax::ast::ClassPerlKind::Word),
3285            'W' => (true, regex_syntax::ast::ClassPerlKind::Word),
3286            c => panic!("expected valid Perl class but got '{}'", c),
3287        };
3288        ClassPerl {
3289            span,
3290            kind,
3291            negated,
3292        }
3293    }
3294}
3295
3296fn is_universal_perl_pair(item: &regex_syntax::ast::ClassSetItem) -> bool {
3297    use regex_syntax::ast::ClassSetItem;
3298    let items = match item {
3299        ClassSetItem::Union(u) => &u.items,
3300        _ => return false,
3301    };
3302    if items.len() != 2 {
3303        return false;
3304    }
3305    match (&items[0], &items[1]) {
3306        (ClassSetItem::Perl(a), ClassSetItem::Perl(b)) => {
3307            let is_all = a.kind == b.kind && a.negated != b.negated;
3308            is_all
3309        }
3310        _ => false,
3311    }
3312}
3313
3314pub fn max_concat_length(ast: &ast::Ast) -> usize {
3315    match ast {
3316        ast::Ast::Empty(_)
3317        | ast::Ast::Flags(_)
3318        | ast::Ast::Literal(_)
3319        | ast::Ast::Dot(_)
3320        | ast::Ast::Top(_)
3321        | ast::Ast::Assertion(_)
3322        | ast::Ast::ClassUnicode(_)
3323        | ast::Ast::ClassPerl(_)
3324        | ast::Ast::ClassBracketed(_) => 0,
3325        ast::Ast::Group(g) => max_concat_length(&g.ast),
3326        ast::Ast::Complement(c) => max_concat_length(&c.ast),
3327        ast::Ast::Lookaround(l) => max_concat_length(&l.ast),
3328        ast::Ast::Repetition(r) => max_concat_length(&r.ast),
3329        ast::Ast::Concat(c) => c
3330            .asts
3331            .len()
3332            .max(c.asts.iter().map(max_concat_length).max().unwrap_or(0)),
3333        ast::Ast::Alternation(a) => a.asts.iter().map(max_concat_length).max().unwrap_or(0),
3334        ast::Ast::Intersection(i) => i.asts.iter().map(max_concat_length).max().unwrap_or(0),
3335    }
3336}
3337
3338pub fn expanded_ast_size(ast: &ast::Ast, limit: u64) -> u64 {
3339    fn go(ast: &ast::Ast, limit: u64) -> u64 {
3340        match ast {
3341            ast::Ast::Empty(_) | ast::Ast::Flags(_) => 1,
3342            ast::Ast::Literal(_) | ast::Ast::Dot(_) | ast::Ast::Top(_) => 1,
3343            ast::Ast::Assertion(_) => 1,
3344            ast::Ast::ClassUnicode(_) | ast::Ast::ClassPerl(_) | ast::Ast::ClassBracketed(_) => 1,
3345            ast::Ast::Group(g) => go(&g.ast, limit).saturating_add(1).min(limit),
3346            ast::Ast::Complement(c) => go(&c.ast, limit).saturating_add(1).min(limit),
3347            ast::Ast::Lookaround(l) => go(&l.ast, limit).saturating_add(1).min(limit),
3348            ast::Ast::Concat(c) => sum_children(&c.asts, limit),
3349            ast::Ast::Alternation(a) => sum_children(&a.asts, limit),
3350            ast::Ast::Intersection(i) => sum_children(&i.asts, limit),
3351            ast::Ast::Repetition(r) => {
3352                let body = go(&r.ast, limit);
3353                let factor: u64 = match &r.op.kind {
3354                    ast::RepetitionKind::ZeroOrOne => 2,
3355                    ast::RepetitionKind::ZeroOrMore | ast::RepetitionKind::OneOrMore => 2,
3356                    ast::RepetitionKind::Range(ast::RepetitionRange::Exactly(n)) => {
3357                        (*n as u64).max(1)
3358                    }
3359                    ast::RepetitionKind::Range(ast::RepetitionRange::AtLeast(n)) => {
3360                        (*n as u64).max(1).saturating_add(1)
3361                    }
3362                    ast::RepetitionKind::Range(ast::RepetitionRange::Bounded(_, m)) => {
3363                        (*m as u64).max(1)
3364                    }
3365                };
3366                body.saturating_mul(factor).min(limit)
3367            }
3368        }
3369    }
3370    fn sum_children(children: &[ast::Ast], limit: u64) -> u64 {
3371        let mut total: u64 = 0;
3372        for c in children {
3373            total = total.saturating_add(go(c, limit));
3374            if total >= limit {
3375                return limit;
3376            }
3377        }
3378        total
3379    }
3380    go(ast, limit)
3381}
3382
3383pub fn parse_ast<'s>(tb: &mut TB<'s>, pattern: &'s str) -> std::result::Result<NodeId, ParseError> {
3384    let mut p: ResharpParser<'s> = ResharpParser::new(pattern);
3385    p.parse(tb)
3386}
3387
3388pub fn parse_ast_with<'s>(
3389    tb: &mut TB<'s>,
3390    pattern: &'s str,
3391    flags: &PatternFlags,
3392) -> std::result::Result<NodeId, ParseError> {
3393    let mut p: ResharpParser<'s> = ResharpParser::with_flags(pattern, flags);
3394    p.parse(tb)
3395}
3396
3397/// Parse a pattern into the raw AST without converting to algebra nodes.
3398pub fn parse_to_ast(pattern: &str) -> std::result::Result<ast::Ast, ParseError> {
3399    let mut p: ResharpParser = ResharpParser::new(pattern);
3400    p.parse_inner()
3401}