swc_ecma_regexp/parser/
flags_parser.rs

1use rustc_hash::FxHashSet;
2
3use crate::{
4    diagnostics::{self, Result},
5    parser::{reader::Reader, span_factory::SpanFactory},
6};
7
8pub struct FlagsParser<'a> {
9    reader: Reader<'a>,
10    span_factory: SpanFactory,
11}
12
13impl<'a> FlagsParser<'a> {
14    pub fn new(reader: Reader<'a>, span_offset: u32) -> Self {
15        Self {
16            reader,
17            span_factory: SpanFactory::new(span_offset),
18        }
19    }
20
21    /// Returns: (is_unicode_mode, is_unicode_sets_mode)
22    pub fn parse(mut self) -> Result<(bool, bool)> {
23        let mut is_unicode_mode = false;
24        let mut is_unicode_sets_mode = false;
25        let mut unique_flags = FxHashSet::default();
26
27        while let Some(cp) = self.reader.peek() {
28            let span_start = self.reader.offset();
29            self.reader.advance();
30            let span_end = self.reader.offset();
31
32            if unique_flags.contains(&cp) {
33                return Err(diagnostics::duplicated_flags(
34                    self.span_factory.create(span_start, span_end),
35                    &self.reader.atom(span_start, span_end),
36                ));
37            }
38            if char::try_from(cp).map_or(true, |c| {
39                !matches!(c, 'd' | 'g' | 'i' | 'm' | 's' | 'u' | 'v' | 'y')
40            }) {
41                return Err(diagnostics::unknown_flag(
42                    self.span_factory.create(span_start, span_end),
43                    &self.reader.atom(span_start, span_end),
44                ));
45            }
46
47            if cp == 'u' as u32 {
48                if unique_flags.contains(&('v' as u32)) {
49                    return Err(diagnostics::invalid_unicode_flags(
50                        self.span_factory.create(span_start, span_end),
51                    ));
52                }
53                is_unicode_mode = true;
54            }
55            if cp == 'v' as u32 {
56                if unique_flags.contains(&('u' as u32)) {
57                    return Err(diagnostics::invalid_unicode_flags(
58                        self.span_factory.create(span_start, span_end),
59                    ));
60                }
61                is_unicode_mode = true;
62                is_unicode_sets_mode = true;
63            }
64
65            unique_flags.insert(cp);
66        }
67
68        Ok((is_unicode_mode, is_unicode_sets_mode))
69    }
70}
71
72#[cfg(test)]
73mod test {
74    use super::*;
75
76    #[test]
77    fn should_pass() {
78        for (flags_text, expected) in &[
79            ("", (false, false)),
80            ("i", (false, false)),
81            ("u", (true, false)),
82            ("v", (true, true)),
83            ("vg", (true, true)),
84        ] {
85            let reader = Reader::initialize(flags_text, true, false).unwrap();
86            let result = FlagsParser::new(reader, 0).parse().unwrap();
87            assert_eq!(result, *expected);
88        }
89    }
90
91    #[test]
92    fn should_fail() {
93        for flags_text in &["uv", "vu", "uu", "vv", "gg", "$"] {
94            let reader = Reader::initialize(flags_text, true, false).unwrap();
95            let err = FlagsParser::new(reader, 0).parse();
96            assert!(err.is_err());
97            // println!("{:?}", err.unwrap_err().with_source_code(*flags_text));
98        }
99        for flags_text in &[r#""uv""#, "'V'", "\"-\"", r#""\162""#] {
100            let reader = Reader::initialize(flags_text, true, true).unwrap();
101            let err = FlagsParser::new(reader, 0).parse();
102            assert!(err.is_err());
103            // println!("{:?}", err.unwrap_err().with_source_code(*flags_text));
104        }
105    }
106
107    #[test]
108    fn string_literal() {
109        for reader in [
110            Reader::initialize("u", true, false).unwrap(),
111            Reader::initialize("'u'", true, true).unwrap(),
112            Reader::initialize(r#""\165""#, true, true).unwrap(),
113            Reader::initialize(r#""\x75""#, true, true).unwrap(),
114            Reader::initialize(r#""\u0075""#, true, true).unwrap(),
115            Reader::initialize(r#""\u{0075}""#, true, true).unwrap(),
116        ] {
117            let result = FlagsParser::new(reader, 0).parse().unwrap();
118            assert_eq!(result, (true, false));
119        }
120    }
121}