1use hermes_atom_table::AtomBytes;
18use hermes_support::location::{SMLoc, SMRange, SourceId};
19
20use crate::token_kinds::TokenKind;
21
22#[derive(Copy, Clone, Debug)]
24pub struct RegExpLiteral {
25 body: AtomBytes,
26 flags: AtomBytes,
27}
28
29impl RegExpLiteral {
30 pub fn new(body: AtomBytes, flags: AtomBytes) -> RegExpLiteral {
32 RegExpLiteral { body, flags }
33 }
34 pub fn body(&self) -> AtomBytes {
36 self.body
37 }
38 pub fn flags(&self) -> AtomBytes {
40 self.flags
41 }
42}
43
44#[derive(Clone, Debug)]
48pub struct Token {
49 kind: TokenKind,
50 range: SMRange,
51 numeric: f64,
52 ident: Option<AtomBytes>,
53
54 string_literal: Option<AtomBytes>,
58
59 regexp: Option<RegExpLiteral>,
60
61 raw_string: Option<AtomBytes>,
66
67 string_literal_contains_escapes: bool,
71}
72
73impl Token {
74 pub fn new(source: SourceId) -> Token {
76 let loc = SMLoc { source, offset: 0 };
77 Token {
78 kind: TokenKind::none,
79 range: SMRange {
80 start: loc,
81 end: loc,
82 },
83 numeric: 0.0,
84 ident: None,
85 string_literal: None,
86 regexp: None,
87 raw_string: None,
88 string_literal_contains_escapes: false,
89 }
90 }
91
92 pub fn kind(&self) -> TokenKind {
94 self.kind
95 }
96 pub fn is_res_word(&self) -> bool {
98 self.kind.is_res_word()
99 }
100 pub fn is_template_literal(&self) -> bool {
102 matches!(
103 self.kind,
104 TokenKind::no_substitution_template
105 | TokenKind::template_head
106 | TokenKind::template_middle
107 | TokenKind::template_tail
108 )
109 }
110
111 pub fn start_loc(&self) -> SMLoc {
113 self.range.start
114 }
115 pub fn end_loc(&self) -> SMLoc {
117 self.range.end
118 }
119 pub fn source_range(&self) -> SMRange {
121 self.range
122 }
123
124 pub fn get_numeric_literal(&self) -> f64 {
126 debug_assert_eq!(self.kind, TokenKind::numeric_literal);
127 self.numeric
128 }
129
130 pub fn get_identifier(&self) -> AtomBytes {
132 debug_assert_eq!(self.kind, TokenKind::identifier);
133 self.ident.unwrap()
134 }
135 pub fn get_private_identifier(&self) -> AtomBytes {
138 debug_assert_eq!(self.kind, TokenKind::private_identifier);
139 self.ident.unwrap()
140 }
141 pub fn get_res_word_identifier(&self) -> AtomBytes {
143 debug_assert!(self.is_res_word());
144 self.ident.unwrap()
145 }
146 pub fn get_res_word_or_identifier(&self) -> AtomBytes {
149 debug_assert!(self.kind == TokenKind::identifier || self.is_res_word());
150 self.ident.unwrap()
151 }
152
153 pub fn get_string_literal(&self) -> AtomBytes {
156 debug_assert_eq!(self.kind, TokenKind::string_literal);
157 self.string_literal.unwrap()
158 }
159 pub fn get_string_literal_raw_value(&self) -> AtomBytes {
162 debug_assert_eq!(self.kind, TokenKind::string_literal);
163 self.raw_string.unwrap()
164 }
165 pub fn get_string_literal_contains_escapes(&self) -> bool {
170 debug_assert_eq!(self.kind, TokenKind::string_literal);
171 self.string_literal_contains_escapes
172 }
173
174 pub fn get_template_literal_contains_not_escapes(&self) -> bool {
176 debug_assert!(self.is_template_literal());
177 self.string_literal.is_none()
178 }
179 pub fn get_template_value(&self) -> Option<AtomBytes> {
182 debug_assert!(self.is_template_literal());
183 self.string_literal
184 }
185 pub fn get_template_raw_value(&self) -> AtomBytes {
187 debug_assert!(self.is_template_literal());
188 self.raw_string.unwrap()
189 }
190
191 pub fn get_bigint_literal(&self) -> AtomBytes {
196 debug_assert_eq!(self.kind, TokenKind::bigint_literal);
197 self.string_literal.unwrap()
198 }
199 pub fn get_bigint_literal_raw_value(&self) -> AtomBytes {
202 debug_assert_eq!(self.kind, TokenKind::bigint_literal);
203 self.raw_string.unwrap()
204 }
205
206 pub fn get_regexp_literal(&self) -> RegExpLiteral {
208 debug_assert_eq!(self.kind, TokenKind::regexp_literal);
209 self.regexp.unwrap()
210 }
211
212 pub fn get_jsx_text_value(&self) -> AtomBytes {
214 debug_assert_eq!(self.kind, TokenKind::jsx_text);
215 self.string_literal.unwrap()
216 }
217 pub fn get_jsx_text_raw(&self) -> AtomBytes {
220 debug_assert_eq!(self.kind, TokenKind::jsx_text);
221 self.raw_string.unwrap()
222 }
223
224 pub(crate) fn set_start(&mut self, start: SMLoc) {
227 self.range.start = start;
228 }
229 pub(crate) fn set_end(&mut self, end: SMLoc) {
230 self.range.end = end;
231 }
232 pub(crate) fn set_range(&mut self, range: SMRange) {
233 self.range = range;
234 }
235
236 pub(crate) fn set_punctuator(&mut self, kind: TokenKind) {
237 self.kind = kind;
238 }
239 pub(crate) fn set_ident_op(&mut self, kind: TokenKind) {
241 self.kind = kind;
242 }
243 pub(crate) fn set_eof(&mut self) {
244 self.kind = TokenKind::eof;
245 }
246
247 pub(crate) fn set_bigint_literal(&mut self, bigint: AtomBytes, raw: AtomBytes) {
248 self.kind = TokenKind::bigint_literal;
249 self.string_literal = Some(bigint);
250 self.raw_string = Some(raw);
251 }
252 pub(crate) fn set_numeric_literal(&mut self, literal: f64) {
253 self.kind = TokenKind::numeric_literal;
254 self.numeric = literal;
255 }
256 pub(crate) fn set_identifier(&mut self, ident: AtomBytes) {
257 self.kind = TokenKind::identifier;
258 self.ident = Some(ident);
259 }
260 pub(crate) fn set_private_identifier(&mut self, ident: AtomBytes) {
261 self.kind = TokenKind::private_identifier;
262 self.ident = Some(ident);
263 }
264 pub(crate) fn set_string_literal(&mut self, literal: AtomBytes, contains_escapes: bool) {
265 self.kind = TokenKind::string_literal;
266 self.string_literal = Some(literal);
267 self.string_literal_contains_escapes = contains_escapes;
268 }
269 #[allow(dead_code)]
274 pub(crate) fn set_jsx_string_literal(&mut self, literal: AtomBytes, raw: AtomBytes) {
275 self.kind = TokenKind::string_literal;
276 self.string_literal = Some(literal);
277 self.raw_string = Some(raw);
278 self.string_literal_contains_escapes = false;
279 }
280 pub(crate) fn set_regexp_literal(&mut self, literal: RegExpLiteral) {
281 self.kind = TokenKind::regexp_literal;
282 self.regexp = Some(literal);
283 }
284 pub(crate) fn set_res_word(&mut self, kind: TokenKind, ident: AtomBytes) {
285 debug_assert!(kind.is_res_word());
286 self.kind = kind;
287 self.ident = Some(ident);
288 }
289 pub(crate) fn set_template_literal(
290 &mut self,
291 kind: TokenKind,
292 cooked: Option<AtomBytes>,
293 raw: AtomBytes,
294 ) {
295 debug_assert!(matches!(
296 kind,
297 TokenKind::no_substitution_template
298 | TokenKind::template_head
299 | TokenKind::template_middle
300 | TokenKind::template_tail
301 ));
302 self.kind = kind;
303 self.string_literal = cooked;
304 self.raw_string = Some(raw);
305 }
306 pub(crate) fn set_jsx_text(&mut self, value: AtomBytes, raw: AtomBytes) {
307 self.kind = TokenKind::jsx_text;
308 self.string_literal = Some(value);
309 self.raw_string = Some(raw);
310 }
311}
312
313#[derive(Copy, Clone, Eq, PartialEq, Debug)]
315pub enum CommentKind {
316 Line,
318 Block,
320 Hashbang,
322}
323
324#[derive(Copy, Clone, Debug)]
326pub struct StoredComment {
327 kind: CommentKind,
328 range: SMRange,
329}
330
331impl StoredComment {
332 pub fn new(kind: CommentKind, range: SMRange) -> StoredComment {
334 StoredComment { kind, range }
335 }
336 pub fn kind(&self) -> CommentKind {
338 self.kind
339 }
340 pub fn source_range(&self) -> SMRange {
342 self.range
343 }
344
345 pub fn get_string<'a>(&self, buffer: &'a [u8]) -> &'a [u8] {
352 let start = self.range.start.offset as usize + 2;
354 let end = if self.kind == CommentKind::Block {
356 self.range.end.offset as usize - 2
357 } else {
358 self.range.end.offset as usize
359 };
360 debug_assert!(end >= start, "invalid comment range");
361 &buffer[start..end]
362 }
363
364 pub fn get_full_string<'a>(&self, buffer: &'a [u8]) -> &'a [u8] {
371 &buffer[self.range.start.offset as usize..self.range.end.offset as usize]
372 }
373}
374
375#[derive(Copy, Clone, Debug)]
377pub struct StoredToken {
378 kind: TokenKind,
379 range: SMRange,
380}
381
382impl StoredToken {
383 pub fn new(kind: TokenKind, range: SMRange) -> StoredToken {
385 StoredToken { kind, range }
386 }
387 pub fn kind(&self) -> TokenKind {
389 self.kind
390 }
391 pub fn source_range(&self) -> SMRange {
393 self.range
394 }
395}
396
397#[cfg(test)]
398mod tests {
399 use super::*;
400 use crate::token_kinds::TokenKind;
401 use hermes_support::location::{SMLoc, SMRange, SourceId};
402
403 #[test]
404 fn punctuator_token() {
405 let id = SourceId::from_index(0);
406 let mut t = Token::new(id);
407 t.set_punctuator(TokenKind::l_brace);
408 t.set_range(SMRange {
409 start: SMLoc {
410 source: id,
411 offset: 0,
412 },
413 end: SMLoc {
414 source: id,
415 offset: 1,
416 },
417 });
418 assert_eq!(t.kind(), TokenKind::l_brace);
419 assert_eq!(t.start_loc().offset, 0);
420 assert_eq!(t.end_loc().offset, 1);
421 }
422
423 #[test]
424 fn jsx_string_literal_value_and_raw() {
425 let tab = hermes_atom_table::AtomTable::new();
429 let value = tab.atom_bytes(b"a<b");
430 let raw = tab.atom_bytes(b"a<b");
431 let mut t = Token::new(SourceId::from_index(0));
432 t.set_jsx_string_literal(value, raw);
433 assert_eq!(t.kind(), TokenKind::string_literal);
434 assert_eq!(t.get_string_literal(), value);
435 assert_eq!(t.get_string_literal_raw_value(), raw);
436 assert!(!t.get_string_literal_contains_escapes());
437 }
438
439 #[test]
440 fn template_literal_contains_not_escapes() {
441 let tab = hermes_atom_table::AtomTable::new();
443 let raw = tab.atom_bytes(b"\\9");
444 let mut t = Token::new(SourceId::from_index(0));
445 t.set_template_literal(TokenKind::no_substitution_template, None, raw);
446 assert!(t.get_template_literal_contains_not_escapes());
447 let cooked = tab.atom_bytes(b"ok");
448 t.set_template_literal(TokenKind::template_head, Some(cooked), raw);
449 assert!(!t.get_template_literal_contains_not_escapes());
450 assert_eq!(t.get_template_value(), Some(cooked));
451 }
452
453 #[test]
454 fn stored_comment_get_string() {
455 let id = SourceId::from_index(0);
456 let loc = |off| SMLoc {
457 source: id,
458 offset: off,
459 };
460 let buffer = b"// hello /* world */ rest";
464 let line = StoredComment::new(
466 CommentKind::Line,
467 SMRange {
468 start: loc(0),
469 end: loc(8),
470 },
471 );
472 assert_eq!(line.get_string(buffer), b" hello");
473 assert_eq!(line.get_full_string(buffer), b"// hello");
474 let block = StoredComment::new(
476 CommentKind::Block,
477 SMRange {
478 start: loc(9),
479 end: loc(20),
480 },
481 );
482 assert_eq!(block.get_string(buffer), b" world ");
483 assert_eq!(block.get_full_string(buffer), b"/* world */");
484 }
485}