swc_ecma_regexp/parser/
flags_parser.rs1use 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 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 }
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 }
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}