boa_parser/lexer/
regex.rs1use crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer};
4use crate::source::ReadChar;
5use bitflags::bitflags;
6use boa_ast::PositionGroup;
7use boa_interner::Interner;
8use regress::Flags;
9use std::fmt::{Display, Write};
10use std::str::{self, FromStr};
11
12const MAXIMUM_REGEX_FLAGS: usize = 8;
13
14#[derive(Debug, Clone, Copy)]
27pub(super) struct RegexLiteral {
28 init_with_eq: bool,
31}
32
33impl RegexLiteral {
34 pub(super) fn new(init_with_eq: bool) -> Self {
36 Self { init_with_eq }
37 }
38}
39
40impl<R> Tokenizer<R> for RegexLiteral {
41 fn lex(
42 &mut self,
43 cursor: &mut Cursor<R>,
44 start_pos: PositionGroup,
45 interner: &mut Interner,
46 ) -> Result<Token, Error>
47 where
48 R: ReadChar,
49 {
50 let mut body = Vec::new();
51 if self.init_with_eq {
52 body.push(u32::from(b'='));
53 }
54
55 let mut is_class_char = false;
56
57 loop {
59 match cursor.next_char()? {
60 None => {
61 return Err(Error::syntax(
63 "abrupt end on regular expression",
64 cursor.pos(),
65 ));
66 }
67 Some(b) => {
68 match b {
69 0x2F if !is_class_char => break, 0x5B => {
73 is_class_char = true;
74 body.push(b);
75 }
76 0x5D if is_class_char => {
78 is_class_char = false;
79 body.push(b);
80 }
81 0xA | 0xD | 0x2028 | 0x2029 => {
83 return Err(Error::syntax(
85 "new lines are not allowed in regular expressions",
86 cursor.pos(),
87 ));
88 }
89 0x5C => {
91 body.push(b);
93 if let Some(sc) = cursor.next_char()? {
94 match sc {
95 0xA | 0xD | 0x2028 | 0x2029 => {
97 return Err(Error::syntax(
99 "new lines are not allowed in regular expressions",
100 cursor.pos(),
101 ));
102 }
103 b => body.push(b),
104 }
105 } else {
106 return Err(Error::syntax(
108 "abrupt end on regular expression",
109 cursor.pos(),
110 ));
111 }
112 }
113 _ => body.push(b),
114 }
115 }
116 }
117 }
118
119 let mut flags: [u32; MAXIMUM_REGEX_FLAGS] = [0; MAXIMUM_REGEX_FLAGS];
120 let n = cursor.take_array_alphabetic(&mut flags)?;
121 if n > MAXIMUM_REGEX_FLAGS {
122 return Err(Error::syntax(
124 "Invalid regular expression: too many flags",
125 start_pos,
126 ));
127 }
128 let flags: RegExpFlags =
129 RegExpFlags::try_from(&flags[..n]).map_err(|e| Error::syntax(e, start_pos))?;
130
131 let mut body_utf16 = Vec::with_capacity(body.len());
133
134 #[allow(clippy::cast_possible_truncation)]
137 for cp in &body {
138 let cp = *cp;
139 if cp <= 0xFFFF {
140 body_utf16.push(cp as u16);
141 } else {
142 let cp = cp - 0x1_0000;
143 let high = 0xD800 | ((cp >> 10) as u16);
144 let low = 0xDC00 | ((cp as u16) & 0x3FF);
145 body_utf16.push(high);
146 body_utf16.push(low);
147 }
148 }
149
150 drop(
152 regress::backends::try_parse(body.into_iter(), flags.into()).map_err(|error| {
153 Error::syntax(
154 format!("Invalid regular expression literal: {error}"),
155 start_pos,
156 )
157 })?,
158 );
159
160 Ok(Token::new_by_position_group(
161 TokenKind::regular_expression_literal(
162 interner.get_or_intern(body_utf16.as_slice()),
163 interner.get_or_intern(flags.to_string().as_str()),
164 ),
165 start_pos,
166 cursor.pos_group(),
167 ))
168 }
169}
170
171bitflags! {
172 #[derive(Debug, Default, Copy, Clone)]
174 pub struct RegExpFlags: u8 {
175 const GLOBAL = 0b0000_0001;
178
179 const IGNORE_CASE = 0b0000_0010;
181
182 const MULTILINE = 0b0000_0100;
184
185 const DOT_ALL = 0b0000_1000;
187
188 const UNICODE = 0b0001_0000;
190
191 const STICKY = 0b0010_0000;
193
194 const HAS_INDICES = 0b0100_0000;
197
198 const UNICODE_SETS = 0b1000_0000;
200 }
201}
202
203impl TryFrom<&[u32]> for RegExpFlags {
204 type Error = String;
205
206 fn try_from(value: &[u32]) -> Result<Self, Self::Error> {
207 let mut flags = Self::default();
208 for c in value {
209 let c = char::from_u32(*c)
210 .ok_or_else(|| format!("Invalid regular expression flag: {c}"))?;
211
212 let new_flag = match c {
213 'g' => Self::GLOBAL,
214 'i' => Self::IGNORE_CASE,
215 'm' => Self::MULTILINE,
216 's' => Self::DOT_ALL,
217 'u' => Self::UNICODE,
218 'y' => Self::STICKY,
219 'd' => Self::HAS_INDICES,
220 'v' => Self::UNICODE_SETS,
221 _ => return Err(format!("invalid regular expression flag {c}")),
222 };
223
224 if flags.contains(new_flag) {
225 return Err(format!("repeated regular expression flag {c}"));
226 }
227 flags.insert(new_flag);
228 }
229
230 if flags.contains(Self::UNICODE) && flags.contains(Self::UNICODE_SETS) {
231 return Err("cannot use both 'u' and 'v' flags".into());
232 }
233
234 Ok(flags)
235 }
236}
237
238impl FromStr for RegExpFlags {
239 type Err = String;
240
241 fn from_str(s: &str) -> Result<Self, Self::Err> {
242 let mut flags = Self::default();
243 for c in s.bytes() {
244 let new_flag = match c {
245 b'g' => Self::GLOBAL,
246 b'i' => Self::IGNORE_CASE,
247 b'm' => Self::MULTILINE,
248 b's' => Self::DOT_ALL,
249 b'u' => Self::UNICODE,
250 b'y' => Self::STICKY,
251 b'd' => Self::HAS_INDICES,
252 b'v' => Self::UNICODE_SETS,
253 _ => return Err(format!("invalid regular expression flag {}", char::from(c))),
254 };
255
256 if flags.contains(new_flag) {
257 return Err(format!(
258 "repeated regular expression flag {}",
259 char::from(c)
260 ));
261 }
262 flags.insert(new_flag);
263 }
264
265 if flags.contains(Self::UNICODE) && flags.contains(Self::UNICODE_SETS) {
266 return Err("cannot use both 'u' and 'v' flags".into());
267 }
268
269 Ok(flags)
270 }
271}
272
273impl Display for RegExpFlags {
274 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
275 if self.contains(Self::HAS_INDICES) {
276 f.write_char('d')?;
277 }
278 if self.contains(Self::GLOBAL) {
279 f.write_char('g')?;
280 }
281 if self.contains(Self::IGNORE_CASE) {
282 f.write_char('i')?;
283 }
284 if self.contains(Self::MULTILINE) {
285 f.write_char('m')?;
286 }
287 if self.contains(Self::DOT_ALL) {
288 f.write_char('s')?;
289 }
290 if self.contains(Self::UNICODE) {
291 f.write_char('u')?;
292 }
293 if self.contains(Self::STICKY) {
294 f.write_char('y')?;
295 }
296 if self.contains(Self::UNICODE_SETS) {
297 f.write_char('v')?;
298 }
299 Ok(())
300 }
301}
302
303impl From<RegExpFlags> for Flags {
304 fn from(value: RegExpFlags) -> Self {
305 Self {
306 icase: value.contains(RegExpFlags::IGNORE_CASE),
307 multiline: value.contains(RegExpFlags::MULTILINE),
308 dot_all: value.contains(RegExpFlags::DOT_ALL),
309 unicode: value.contains(RegExpFlags::UNICODE),
310 unicode_sets: value.contains(RegExpFlags::UNICODE_SETS),
311 ..Self::default()
312 }
313 }
314}