1mod dump;
19mod escape;
20mod identifier;
21mod jsx;
22mod lookahead;
23mod number;
24mod regexp;
25mod state;
26mod string;
27mod template;
28
29pub use state::SavePoint;
30
31use std::rc::Rc;
32
33use hermes_atom_table::{AtomBytes, AtomTable};
34use hermes_support::buffer::SourceBuffer;
35use hermes_support::diag::Subsystem;
36use hermes_support::location::{SMLoc, SMRange, SourceId};
37use hermes_support::manager::SourceErrorManager;
38
39use hermes_unicode::{
40 is_unicode_id_start, is_unicode_only_id_start, is_unicode_only_space,
41};
42
43use crate::cursor::Cursor;
44use crate::token::{CommentKind, StoredComment, StoredToken, Token};
45use crate::token_kinds::TokenKind;
46use crate::utf8::{
47 append_unicode_to_storage, convert_utf16_to_utf8_with_replacements,
48 convert_utf8_with_surrogates_to_utf16, decode_utf8,
49 match_unicode_line_terminator_offset1, UTF8_LINE_TERMINATOR_CHAR0,
50};
51
52#[derive(Copy, Clone, Eq, PartialEq, Debug)]
56pub enum GrammarContext {
57 AllowRegExp,
59 AllowDiv,
61 AllowJSXIdentifier,
64 Type,
67}
68
69#[derive(Copy, Clone, Eq, PartialEq, Debug)]
73pub enum IdentifierMode {
74 JS,
76 JSX,
78 Flow,
80}
81
82pub(crate) trait IdMode {
89 const MODE: IdentifierMode;
90}
91pub(crate) struct JsMode;
93impl IdMode for JsMode {
94 const MODE: IdentifierMode = IdentifierMode::JS;
95}
96pub(crate) struct JsxMode;
98impl IdMode for JsxMode {
99 const MODE: IdentifierMode = IdentifierMode::JSX;
100}
101pub(crate) struct FlowMode;
103impl IdMode for FlowMode {
104 const MODE: IdentifierMode = IdentifierMode::Flow;
105}
106
107pub struct JSLexer<'a> {
112 sm: &'a mut SourceErrorManager,
113 buf_id: SourceId,
115 cursor: Cursor,
117 strtab: &'a AtomTable,
119
120 res_word_idents: Vec<AtomBytes>,
123
124 token: Token,
126 prev_token_end: SMLoc,
128 new_line_before_current_token: bool,
130
131 strict_mode: bool,
135 convert_surrogates: bool,
138
139 tmp_storage: Vec<u8>,
142
143 raw_storage: Vec<u8>,
146
147 source_url: Option<String>,
149 source_mapping_url: Option<String>,
151
152 store_comments: bool,
155 comment_storage: Vec<StoredComment>,
158 store_tokens: bool,
161 token_storage: Vec<StoredToken>,
164}
165
166impl<'a> JSLexer<'a> {
167 pub fn new(
172 buf_id: SourceId,
173 sm: &'a mut SourceErrorManager,
174 strtab: &'a AtomTable,
175 grammar_context: GrammarContext,
176 ) -> JSLexer<'a> {
177 JSLexer::new_with_convert_surrogates(
178 buf_id,
179 sm,
180 strtab,
181 grammar_context,
182 false,
183 )
184 }
185
186 pub fn new_with_convert_surrogates(
192 buf_id: SourceId,
193 sm: &'a mut SourceErrorManager,
194 strtab: &'a AtomTable,
195 _grammar_context: GrammarContext,
196 convert_surrogates: bool,
197 ) -> JSLexer<'a> {
198 let buffer: Rc<SourceBuffer> = sm.source_buffer(buf_id);
199 let cursor = Cursor::new(buffer);
200 let start = SMLoc {
201 source: buf_id,
202 offset: 0,
203 };
204 let mut lexer = JSLexer {
205 sm,
206 buf_id,
207 cursor,
208 strtab,
209 res_word_idents: Vec::new(),
210 token: Token::new(buf_id),
211 prev_token_end: start,
212 new_line_before_current_token: false,
213 strict_mode: true,
214 convert_surrogates,
215 tmp_storage: Vec::new(),
216 raw_storage: Vec::new(),
217 source_url: None,
218 source_mapping_url: None,
219 store_comments: false,
220 comment_storage: Vec::new(),
221 store_tokens: false,
222 token_storage: Vec::new(),
223 };
224 lexer.initialize_reserved_identifiers();
225 lexer
226 }
227
228 fn convert_surrogates_in_string(&self, bytes: &[u8]) -> AtomBytes {
234 let ustr = convert_utf8_with_surrogates_to_utf16(bytes);
235 let output = convert_utf16_to_utf8_with_replacements(&ustr);
236 self.strtab.atom_bytes(output)
237 }
238
239 pub fn get_string_literal(&self, bytes: &[u8]) -> AtomBytes {
243 if self.convert_surrogates {
244 self.convert_surrogates_in_string(bytes)
245 } else {
246 self.strtab.atom_bytes(bytes)
247 }
248 }
249
250 fn initialize_reserved_identifiers(&mut self) {
253 use crate::token_kinds::{ord, token_kind_str_by_ord, TokenKind};
254 let first = ord(TokenKind::_first_resword);
255 let last = ord(TokenKind::_last_resword);
256 let count = (last - first + 1) as usize;
260 self.res_word_idents = Vec::with_capacity(count);
261 for v in first..=last {
262 let name = if v > first && v < last {
265 token_kind_str_by_ord(v)
266 } else {
267 ""
268 };
269 self.res_word_idents
270 .push(self.strtab.atom_bytes(name.as_bytes()));
271 }
272 }
273
274 pub(crate) fn res_word_ident(&self, kind: TokenKind) -> AtomBytes {
277 use crate::token_kinds::{ord, TokenKind as TK};
278 debug_assert!(kind.is_res_word());
279 self.res_word_idents[(ord(kind) - ord(TK::_first_resword)) as usize]
280 }
281
282 pub fn token(&self) -> &Token {
284 &self.token
285 }
286
287 pub fn is_strict_mode(&self) -> bool {
289 self.strict_mode
290 }
291
292 pub fn set_strict_mode(&mut self, strict_mode: bool) {
295 self.strict_mode = strict_mode;
296 }
297
298 pub fn is_new_line_before_current_token(&self) -> bool {
300 self.new_line_before_current_token
301 }
302
303 pub fn set_store_comments(&mut self, store_comments: bool) {
306 self.store_comments = store_comments;
307 }
308
309 pub fn get_store_tokens(&self) -> bool {
311 self.store_tokens
312 }
313
314 pub fn set_store_tokens(&mut self, store_tokens: bool) {
317 self.store_tokens = store_tokens;
318 }
319
320 pub fn store_current_token(&mut self) {
323 debug_assert!(
324 self.store_tokens,
325 "Tokens shouldn't be stored unless the flag is set"
326 );
327 self.token_storage.push(StoredToken::new(
328 self.token.kind(),
329 self.token.source_range(),
330 ));
331 }
332
333 pub fn get_stored_comments(&self) -> &[StoredComment] {
335 &self.comment_storage
336 }
337
338 pub fn move_stored_comments(&mut self) -> Vec<StoredComment> {
341 std::mem::take(&mut self.comment_storage)
342 }
343
344 pub fn get_stored_tokens(&self) -> &[StoredToken] {
346 &self.token_storage
347 }
348
349 pub fn get_source_url(&self) -> Option<&str> {
352 self.source_url.as_deref()
353 }
354
355 pub fn get_source_mapping_url(&self) -> Option<&str> {
358 self.source_mapping_url.as_deref()
359 }
360
361 pub fn prev_token_end(&self) -> SMLoc {
363 self.prev_token_end
364 }
365
366 pub fn get_cur_loc(&self) -> SMLoc {
370 self.cur_loc()
371 }
372
373 pub fn get_buffer_id(&self) -> SourceId {
376 self.buf_id
377 }
378
379 pub fn get_source_mgr(&self) -> &SourceErrorManager {
381 self.sm
382 }
383
384 pub fn get_source_mgr_mut(&mut self) -> &mut SourceErrorManager {
388 self.sm
389 }
390
391 pub fn get_string_table(&self) -> &AtomTable {
395 self.strtab
396 }
397
398 pub fn buffer_bytes(&self) -> &[u8] {
404 let raw = self.cursor.raw();
405 &raw[..raw.len() - 1]
408 }
409
410 pub fn get_buffer_start(&self) -> u32 {
413 0
414 }
415
416 pub fn get_buffer_end(&self) -> u32 {
420 self.buffer_bytes().len() as u32
421 }
422
423 pub fn check_following_character(&self, c: u8) -> bool {
435 debug_assert!(c < 128, "test character must be ASCII");
436 self.cursor.raw()[self.token.end_loc().offset as usize] == c
437 }
438
439 pub fn token_input_str(&self) -> &[u8] {
444 self.cursor.slice(
445 self.token.start_loc().offset,
446 self.token.end_loc().offset,
447 )
448 }
449
450 pub fn get_identifier(&self, name: &[u8]) -> AtomBytes {
453 self.strtab.atom_bytes(name)
454 }
455
456 pub fn convert_cur_token_to_ident_op(&mut self, kind: TokenKind) {
460 debug_assert_eq!(self.token.kind(), TokenKind::identifier);
461 debug_assert_eq!(
462 self.strtab.bytes(self.token.get_identifier()),
463 crate::token_kinds::token_kind_str(kind).as_bytes()
464 );
465 self.token.set_ident_op(kind);
466 }
467
468 #[inline]
470 pub(crate) fn cur_loc(&self) -> SMLoc {
471 SMLoc {
472 source: self.buf_id,
473 offset: self.cursor.offset(),
474 }
475 }
476
477 #[inline]
479 fn set_token_start(&mut self) {
480 let loc = self.cur_loc();
481 self.token.set_start(loc);
482 }
483
484 #[inline]
486 pub fn force_eof(&mut self) {
487 self.cursor.seek_end();
488 }
489
490 #[inline]
494 pub fn seek(&mut self, loc: SMLoc) {
495 self.cursor.seek(loc.offset);
496 }
497
498 pub(crate) fn error(&mut self, loc: SMLoc, msg: impl Into<String>) -> bool {
502 self.sm.error_at(loc, None, msg.into(), Subsystem::Lexer);
503 if !self.sm.is_error_limit_reached() {
504 return true;
505 }
506 self.force_eof();
507 false
508 }
509
510 pub(crate) fn error_range(&mut self, range: SMRange, msg: impl Into<String>) -> bool {
513 self.sm
514 .error_at(range.start, Some(range), msg.into(), Subsystem::Lexer);
515 if !self.sm.is_error_limit_reached() {
516 return true;
517 }
518 self.force_eof();
519 false
520 }
521
522 #[inline]
525 fn finish_token(&mut self) {
526 self.prev_token_end = self.token.end_loc();
527 let end = self.cur_loc();
528 self.token.set_end(end);
529 if self.store_tokens {
530 self.store_current_token();
531 }
532 }
533
534 #[inline]
538 fn punc_l1_1(&mut self, tok: TokenKind) {
539 self.set_token_start();
540 self.token.set_punctuator(tok);
541 self.cursor.advance(1);
542 }
543
544 #[inline]
546 fn punc_l2_2(&mut self, ch2: u8, tok1: TokenKind, tok2: TokenKind) {
547 self.set_token_start();
548 if self.cursor.peek_at(1) == ch2 {
549 self.token.set_punctuator(tok2);
550 self.cursor.advance(2);
551 } else {
552 self.token.set_punctuator(tok1);
553 self.cursor.advance(1);
554 }
555 }
556
557 #[inline]
559 fn punc_l2_3(
560 &mut self,
561 ch2a: u8,
562 tok2a: TokenKind,
563 ch2b: u8,
564 tok2b: TokenKind,
565 tok1: TokenKind,
566 ) {
567 self.set_token_start();
568 let c1 = self.cursor.peek_at(1);
569 if c1 == ch2a {
570 self.token.set_punctuator(tok2a);
571 self.cursor.advance(2);
572 } else if c1 == ch2b {
573 self.token.set_punctuator(tok2b);
574 self.cursor.advance(2);
575 } else {
576 self.token.set_punctuator(tok1);
577 self.cursor.advance(1);
578 }
579 }
580
581 #[inline]
583 fn punc_l3_3(
584 &mut self,
585 ch2: u8,
586 tok2: TokenKind,
587 ch3: u8,
588 tok3: TokenKind,
589 tok1: TokenKind,
590 ) {
591 self.set_token_start();
592 if self.cursor.peek_at(1) != ch2 {
593 self.token.set_punctuator(tok1);
594 self.cursor.advance(1);
595 } else if self.cursor.peek_at(2) == ch3 {
596 self.token.set_punctuator(tok3);
597 self.cursor.advance(3);
598 } else {
599 self.token.set_punctuator(tok2);
600 self.cursor.advance(2);
601 }
602 }
603
604 fn scan_default_non_ascii(&mut self, grammar_context: GrammarContext) -> bool {
619 self.set_token_start();
620 let ch = self.decode_utf8_advance();
621
622 if is_unicode_only_id_start(ch) {
623 self.tmp_storage.clear();
624 append_unicode_to_storage(&mut self.tmp_storage, ch);
625 self.scan_identifier_parts_in_context(grammar_context);
626 false
627 } else if is_unicode_only_space(ch) {
628 true
629 } else {
630 let range = SMRange {
631 start: self.token.start_loc(),
632 end: self.cur_loc(),
633 };
634 if ch > 31 && ch < 127 {
635 self.error_range(
636 range,
637 format!("unrecognized character '{}'", ch as u8 as char),
638 );
639 } else {
640 self.error_range(
641 range,
642 format!("unrecognized Unicode character \\u{:x}", ch),
643 );
644 }
645 true
646 }
647 }
648
649 pub fn advance(&mut self, grammar_context: GrammarContext) -> &Token {
652 self.new_line_before_current_token = false;
653
654 loop {
655 debug_assert!((self.cursor.offset() as usize) < self.cursor.raw().len());
657 let c = self.cursor.peek();
658 match c {
659 0 => {
660 self.set_token_start();
661 #[allow(clippy::if_same_then_else)]
664 if self.cursor.at_end() {
665 self.token.set_eof();
666 } else if !self.error(self.token.start_loc(), "unrecognized Unicode character \\u0000")
667 {
668 self.token.set_eof();
669 } else {
670 self.cursor.advance(1);
671 continue;
672 }
673 }
674
675 b'}' => self.punc_l1_1(TokenKind::r_brace),
677 b'(' => self.punc_l1_1(TokenKind::l_paren),
678 b')' => self.punc_l1_1(TokenKind::r_paren),
679 b'[' => self.punc_l1_1(TokenKind::l_square),
680 b']' => self.punc_l1_1(TokenKind::r_square),
681 b';' => self.punc_l1_1(TokenKind::semi),
682 b',' => self.punc_l1_1(TokenKind::comma),
683 b'~' => self.punc_l1_1(TokenKind::tilde),
684 b':' => self.punc_l1_1(TokenKind::colon),
685
686 b'{' => {
688 self.set_token_start();
689 if grammar_context == GrammarContext::Type
690 && self.cursor.peek_at(1) == b'|'
691 {
692 self.token.set_punctuator(TokenKind::l_bracepipe);
693 self.cursor.advance(2);
694 } else {
695 self.token.set_punctuator(TokenKind::l_brace);
696 self.cursor.advance(1);
697 }
698 }
699
700 b'=' => {
702 self.set_token_start();
703 if self.cursor.peek_at(1) == b'>' {
704 self.token.set_punctuator(TokenKind::equalgreater);
705 self.cursor.advance(2);
706 } else if self.cursor.peek_at(1) != b'=' {
707 self.token.set_punctuator(TokenKind::equal);
708 self.cursor.advance(1);
709 } else if self.cursor.peek_at(2) == b'=' {
710 self.token.set_punctuator(TokenKind::equalequalequal);
711 self.cursor.advance(3);
712 } else {
713 self.token.set_punctuator(TokenKind::equalequal);
714 self.cursor.advance(2);
715 }
716 }
717
718 b'!' => self.punc_l3_3(
720 b'=',
721 TokenKind::exclaimequal,
722 b'=',
723 TokenKind::exclaimequalequal,
724 TokenKind::exclaim,
725 ),
726
727 b'+' => self.punc_l2_3(
729 b'+',
730 TokenKind::plusplus,
731 b'=',
732 TokenKind::plusequal,
733 TokenKind::plus,
734 ),
735 b'-' => self.punc_l2_3(
737 b'-',
738 TokenKind::minusminus,
739 b'=',
740 TokenKind::minusequal,
741 TokenKind::minus,
742 ),
743
744 b'&' => {
746 self.set_token_start();
747 if self.cursor.peek_at(1) == b'&' {
748 if self.cursor.peek_at(2) == b'=' {
749 self.token.set_punctuator(TokenKind::ampampequal);
750 self.cursor.advance(3);
751 } else {
752 self.token.set_punctuator(TokenKind::ampamp);
753 self.cursor.advance(2);
754 }
755 } else if self.cursor.peek_at(1) == b'=' {
756 self.token.set_punctuator(TokenKind::ampequal);
757 self.cursor.advance(2);
758 } else {
759 self.token.set_punctuator(TokenKind::amp);
760 self.cursor.advance(1);
761 }
762 }
763
764 b'|' => {
766 self.set_token_start();
767 if grammar_context == GrammarContext::Type
768 && self.cursor.peek_at(1) == b'}'
769 {
770 self.token.set_punctuator(TokenKind::piper_brace);
771 self.cursor.advance(2);
772 } else if self.cursor.peek_at(1) == b'|' {
773 if self.cursor.peek_at(2) == b'=' {
774 self.token.set_punctuator(TokenKind::pipepipeequal);
775 self.cursor.advance(3);
776 } else {
777 self.token.set_punctuator(TokenKind::pipepipe);
778 self.cursor.advance(2);
779 }
780 } else if self.cursor.peek_at(1) == b'=' {
781 self.token.set_punctuator(TokenKind::pipeequal);
782 self.cursor.advance(2);
783 } else {
784 self.token.set_punctuator(TokenKind::pipe);
785 self.cursor.advance(1);
786 }
787 }
788
789 b'?' => {
791 self.set_token_start();
792 if self.cursor.peek_at(1) == b'.' && !is_ascii_digit(self.cursor.peek_at(2)) {
793 self.token.set_punctuator(TokenKind::questiondot);
798 self.cursor.advance(2);
799 } else if self.cursor.peek_at(1) == b'?'
800 && grammar_context != GrammarContext::Type
801 {
802 if self.cursor.peek_at(2) == b'=' {
803 self.token.set_punctuator(TokenKind::questionquestionequal);
804 self.cursor.advance(3);
805 } else {
806 self.token.set_punctuator(TokenKind::questionquestion);
807 self.cursor.advance(2);
808 }
809 } else {
810 self.token.set_punctuator(TokenKind::question);
811 self.cursor.advance(1);
812 }
813 }
814
815 b'*' => {
817 self.set_token_start();
818 if self.cursor.peek_at(1) == b'=' {
819 self.token.set_punctuator(TokenKind::starequal);
820 self.cursor.advance(2);
821 } else if self.cursor.peek_at(1) != b'*' {
822 self.token.set_punctuator(TokenKind::star);
823 self.cursor.advance(1);
824 } else if self.cursor.peek_at(2) == b'=' {
825 self.token.set_punctuator(TokenKind::starstarequal);
826 self.cursor.advance(3);
827 } else {
828 self.token.set_punctuator(TokenKind::starstar);
829 self.cursor.advance(2);
830 }
831 }
832
833 b'^' => self.punc_l2_2(b'=', TokenKind::caret, TokenKind::caretequal),
835
836 b'%' => {
838 self.set_token_start();
839 let off = self.cursor.offset() as usize;
840 let raw = self.cursor.raw();
841 if grammar_context == GrammarContext::Type
844 && off + 7 < raw.len()
845 && &raw[off..off + 7] == b"%checks"
846 {
847 let ident = self.get_string_literal(b"%checks");
850 self.token.set_identifier(ident);
851 self.cursor.advance(7);
852 } else if self.cursor.peek_at(1) == b'=' {
853 self.token.set_punctuator(TokenKind::percentequal);
854 self.cursor.advance(2);
855 } else {
856 self.token.set_punctuator(TokenKind::percent);
857 self.cursor.advance(1);
858 }
859 }
860
861 b'\r' | b'\n' => {
863 self.cursor.advance(1);
864 self.new_line_before_current_token = true;
865 continue;
866 }
867
868 UTF8_LINE_TERMINATOR_CHAR0 => {
871 if match_unicode_line_terminator_offset1(&self.cursor.raw()[self.cursor.offset() as usize..])
872 {
873 self.cursor.advance(3);
874 self.new_line_before_current_token = true;
875 continue;
876 } else {
877 if self.scan_default_non_ascii(grammar_context) {
879 continue;
880 }
881 }
882 }
883
884 0x0b | 0x0c => {
886 self.cursor.advance(1);
887 continue;
888 }
889
890 b'\t' | b' ' => {
892 loop {
894 self.cursor.advance(1);
895 let n = self.cursor.peek();
896 if n != b'\t' && n != b' ' {
897 break;
898 }
899 }
900 continue;
901 }
902
903 0xc2 => {
905 if self.cursor.peek_at(1) == 0xa0 {
906 self.cursor.advance(2);
907 continue;
908 } else {
909 if self.scan_default_non_ascii(grammar_context) {
911 continue;
912 }
913 }
914 }
915
916 0xef => {
918 if self.cursor.peek_at(1) == 0xbb && self.cursor.peek_at(2) == 0xbf {
919 self.cursor.advance(3);
920 continue;
921 } else {
922 if self.scan_default_non_ascii(grammar_context) {
924 continue;
925 }
926 }
927 }
928
929 b'/' => {
931 if self.cursor.peek_at(1) == b'/' {
932 self.scan_line_comment();
934 continue;
935 } else if self.cursor.peek_at(1) == b'*' {
936 self.skip_block_comment();
938 continue;
939 } else {
940 self.set_token_start();
941 if grammar_context == GrammarContext::AllowRegExp {
942 self.scan_regexp();
943 } else if self.cursor.peek_at(1) == b'=' {
944 self.token.set_punctuator(TokenKind::slashequal);
945 self.cursor.advance(2);
946 } else {
947 self.token.set_punctuator(TokenKind::slash);
948 self.cursor.advance(1);
949 }
950 }
951 }
952
953 b'#' => {
955 if self.cursor.offset() == 0 && self.cursor.peek_at(1) == b'!' {
956 self.scan_line_comment();
958 continue;
959 }
960 self.set_token_start();
961 if !self.scan_private_identifier() {
962 continue;
963 }
964 }
965
966 b'<' => {
968 self.set_token_start();
969 if grammar_context == GrammarContext::Type {
970 self.token.set_punctuator(TokenKind::less);
971 self.cursor.advance(1);
972 } else if self.cursor.peek_at(1) == b'=' {
973 self.token.set_punctuator(TokenKind::lessequal);
974 self.cursor.advance(2);
975 } else if self.cursor.peek_at(1) == b'<' {
976 if self.cursor.peek_at(2) == b'=' {
977 self.token.set_punctuator(TokenKind::lesslessequal);
978 self.cursor.advance(3);
979 } else {
980 self.token.set_punctuator(TokenKind::lessless);
981 self.cursor.advance(2);
982 }
983 } else {
984 self.token.set_punctuator(TokenKind::less);
985 self.cursor.advance(1);
986 }
987 }
988
989 b'>' => {
991 self.set_token_start();
992 if grammar_context == GrammarContext::Type
993 || grammar_context == GrammarContext::AllowJSXIdentifier
994 {
995 self.token.set_punctuator(TokenKind::greater);
996 self.cursor.advance(1);
997 } else if self.cursor.peek_at(1) == b'=' {
998 self.token.set_punctuator(TokenKind::greaterequal);
1000 self.cursor.advance(2);
1001 } else if self.cursor.peek_at(1) == b'>' {
1002 if self.cursor.peek_at(2) == b'=' {
1004 self.token.set_punctuator(TokenKind::greatergreaterequal);
1006 self.cursor.advance(3);
1007 } else if self.cursor.peek_at(2) == b'>' {
1008 if self.cursor.peek_at(3) == b'=' {
1010 self.token
1012 .set_punctuator(TokenKind::greatergreatergreaterequal);
1013 self.cursor.advance(4);
1014 } else {
1015 self.token.set_punctuator(TokenKind::greatergreatergreater);
1016 self.cursor.advance(3);
1017 }
1018 } else {
1019 self.token.set_punctuator(TokenKind::greatergreater);
1020 self.cursor.advance(2);
1021 }
1022 } else {
1023 self.token.set_punctuator(TokenKind::greater);
1024 self.cursor.advance(1);
1025 }
1026 }
1027
1028 b'.' => {
1030 self.set_token_start();
1031 if self.cursor.peek_at(1) >= b'0' && self.cursor.peek_at(1) <= b'9' {
1032 self.scan_number(grammar_context);
1033 } else if self.cursor.peek_at(1) == b'.' && self.cursor.peek_at(2) == b'.' {
1034 self.token.set_punctuator(TokenKind::dotdotdot);
1035 self.cursor.advance(3);
1036 } else {
1037 self.token.set_punctuator(TokenKind::period);
1038 self.cursor.advance(1);
1039 }
1040 }
1041
1042 b'0'..=b'9' => {
1044 self.set_token_start();
1045 self.scan_number(grammar_context);
1046 }
1047
1048 b'_' | b'$' | b'a'..=b'z' | b'A'..=b'Z' => {
1050 self.set_token_start();
1051 let start = self.cursor.offset();
1052 self.scan_identifier_fast_path_in_context(start, grammar_context);
1053 }
1054
1055 b'@' => {
1058 self.set_token_start();
1059 if grammar_context == GrammarContext::Type {
1060 let start = self.cursor.offset();
1061 self.scan_identifier_fast_path_in_context(start, grammar_context);
1062 } else {
1063 self.token.set_punctuator(TokenKind::at);
1064 self.cursor.advance(1);
1065 }
1066 }
1067
1068 b'\\' => {
1071 self.set_token_start();
1072 self.tmp_storage.clear();
1073 let cp = self.consume_unicode_escape();
1074 if !is_unicode_id_start(cp) {
1075 self.error_range(
1076 SMRange {
1077 start: self.token.start_loc(),
1078 end: self.cur_loc(),
1079 },
1080 format!(
1081 "Unicode escape \\u{:x} is not a valid identifier start",
1082 cp
1083 ),
1084 );
1085 continue;
1086 } else {
1087 append_unicode_to_storage(&mut self.tmp_storage, cp);
1088 }
1089 self.scan_identifier_parts_in_context(grammar_context);
1090 }
1091
1092 b'\'' | b'"' => {
1094 self.set_token_start();
1095 self.scan_string_in_context(grammar_context);
1096 }
1097
1098 b'`' => {
1100 self.set_token_start();
1101 self.scan_template_literal();
1102 }
1103
1104 _ => {
1107 if self.scan_default_non_ascii(grammar_context) {
1108 continue;
1109 }
1110 }
1111 }
1112
1113 break;
1115 } self.finish_token();
1118 &self.token
1119 }
1120
1121 fn line_comment_helper(&mut self) -> (u32, u32) {
1128 debug_assert!(
1129 (self.cursor.peek() == b'/' && self.cursor.peek_at(1) == b'/')
1130 || (self.cursor.peek() == b'#' && self.cursor.peek_at(1) == b'!')
1131 );
1132 let start = self.cursor.offset();
1133 let line_comment_end;
1135 self.cursor.advance(2);
1137
1138 loop {
1139 let c = self.cursor.peek();
1140 match c {
1141 0 => {
1142 if self.cursor.at_end() {
1143 line_comment_end = self.cursor.offset();
1144 break;
1145 } else {
1146 self.cursor.advance(1);
1147 }
1148 }
1149 b'\r' | b'\n' => {
1150 line_comment_end = self.cursor.offset();
1151 self.cursor.advance(1);
1152 self.new_line_before_current_token = true;
1153 break;
1154 }
1155 UTF8_LINE_TERMINATOR_CHAR0 => {
1156 if match_unicode_line_terminator_offset1(
1157 &self.cursor.raw()[self.cursor.offset() as usize..],
1158 ) {
1159 line_comment_end = self.cursor.offset();
1160 self.cursor.advance(3);
1161 self.new_line_before_current_token = true;
1162 break;
1163 } else {
1164 self.decode_utf8_skip();
1165 }
1166 }
1167 _ => {
1168 if crate::utf8::is_utf8_start(c) {
1169 self.decode_utf8_skip();
1170 } else {
1171 self.cursor.advance(1);
1172 }
1173 }
1174 }
1175 }
1176
1177 (start, line_comment_end)
1178 }
1179
1180 fn scan_line_comment(&mut self) {
1185 let first = self.cursor.peek();
1186 let (comment_start, comment_end) = self.line_comment_helper();
1187
1188 if self.store_comments {
1189 let kind = if first == b'/' {
1191 CommentKind::Line
1192 } else {
1193 CommentKind::Hashbang
1194 };
1195 self.comment_storage.push(StoredComment::new(
1196 kind,
1197 SMRange {
1198 start: SMLoc {
1199 source: self.buf_id,
1200 offset: comment_start,
1201 },
1202 end: SMLoc {
1203 source: self.buf_id,
1204 offset: comment_end,
1205 },
1206 },
1207 ));
1208 }
1209
1210 let comment = self
1213 .cursor
1214 .slice(comment_start, comment_end)
1215 .to_vec();
1216 let Some(rest) = comment.strip_prefix(b"//# ") else {
1217 return;
1218 };
1219
1220 if let Some(value) = rest.strip_prefix(b"sourceURL=") {
1221 let value = String::from_utf8_lossy(value).into_owned();
1224 self.sm.set_source_url(self.buf_id, &value);
1225 self.source_url = Some(value);
1226 } else if let Some(value) = rest.strip_prefix(b"sourceMappingURL=") {
1227 let value = String::from_utf8_lossy(value).into_owned();
1228 self.sm.set_source_mapping_url(self.buf_id, &value);
1229 self.source_mapping_url = Some(value);
1230 }
1231 }
1232
1233 fn skip_block_comment(&mut self) {
1238 debug_assert!(self.cursor.peek() == b'/' && self.cursor.peek_at(1) == b'*');
1239 let block_comment_start = self.cur_loc();
1240 self.cursor.advance(2);
1242
1243 loop {
1244 let c = self.cursor.peek();
1245 match c {
1246 0 => {
1247 if self.cursor.at_end() {
1248 let loc = self.cur_loc();
1249 self.error(loc, "non-terminated block comment");
1250 self.sm.note(block_comment_start, "comment started here");
1251 break;
1252 } else {
1253 self.cursor.advance(1);
1254 }
1255 }
1256 b'\r' | b'\n' => {
1257 self.cursor.advance(1);
1258 self.new_line_before_current_token = true;
1259 }
1260 UTF8_LINE_TERMINATOR_CHAR0 => {
1261 if match_unicode_line_terminator_offset1(
1262 &self.cursor.raw()[self.cursor.offset() as usize..],
1263 ) {
1264 self.cursor.advance(3);
1265 self.new_line_before_current_token = true;
1266 } else {
1267 self.decode_utf8_skip();
1268 }
1269 }
1270 b'*' => {
1271 self.cursor.advance(1);
1272 if self.cursor.peek() == b'/' {
1273 self.cursor.advance(1);
1274 break;
1275 }
1276 }
1277 _ => {
1278 if crate::utf8::is_utf8_start(c) {
1279 self.decode_utf8_skip();
1280 } else {
1281 self.cursor.advance(1);
1282 }
1283 }
1284 }
1285 }
1286
1287 if self.store_comments {
1288 self.comment_storage.push(StoredComment::new(
1289 CommentKind::Block,
1290 SMRange {
1291 start: block_comment_start,
1292 end: self.cur_loc(),
1293 },
1294 ));
1295 }
1296 }
1297
1298 pub(crate) fn decode_utf8_advance(&mut self) -> u32 {
1302 let save_start = self.cur_loc();
1303 let raw = self.cursor.raw();
1304 let mut i = self.cursor.offset() as usize;
1305 let mut err_msg: Option<String> = None;
1306 let cp = decode_utf8::<false>(raw, &mut i, |m| {
1307 if err_msg.is_none() {
1308 err_msg = Some(m.to_string());
1309 }
1310 });
1311 let consumed = (i - self.cursor.offset() as usize).max(1);
1312 self.cursor.advance(consumed);
1313 if let Some(msg) = err_msg {
1314 self.error(save_start, msg);
1315 }
1316 cp
1317 }
1318
1319 fn decode_utf8_skip(&mut self) {
1324 let raw = self.cursor.raw();
1325 let mut i = self.cursor.offset() as usize;
1326 let _ = decode_utf8::<true>(raw, &mut i, |_| {});
1327 let consumed = i - self.cursor.offset() as usize;
1329 self.cursor.advance(consumed.max(1));
1330 }
1331}
1332
1333#[inline]
1335pub(crate) fn is_ascii_digit(ch: u8) -> bool {
1336 ch.is_ascii_digit()
1337}
1338
1339#[cfg(test)]
1340mod tests {
1341 use super::*;
1342 use hermes_atom_table::AtomTable;
1343 use hermes_support::manager::SourceErrorManager;
1344
1345 #[test]
1346 fn convert_surrogates() {
1347 let mut sm = SourceErrorManager::new();
1350 let id = sm.add_buffer("t", "'\\u{1F600}' '\\uD800'");
1351 let tab = AtomTable::new();
1352 let mut lex = JSLexer::new_with_convert_surrogates(
1353 id,
1354 &mut sm,
1355 &tab,
1356 GrammarContext::AllowDiv,
1357 true,
1358 );
1359 let t = lex.advance(GrammarContext::AllowDiv);
1360 assert_eq!(tab.bytes(t.get_string_literal()), b"\xf0\x9f\x98\x80"); let t = lex.advance(GrammarContext::AllowDiv);
1362 assert_eq!(tab.bytes(t.get_string_literal()), "\u{FFFD}".as_bytes()); let mut sm2 = SourceErrorManager::new();
1367 let id2 = sm2.add_buffer("t2", "'\\u{1F600}'");
1368 let tab2 = AtomTable::new();
1369 let mut lex2 =
1370 JSLexer::new(id2, &mut sm2, &tab2, GrammarContext::AllowDiv);
1371 let t = lex2.advance(GrammarContext::AllowDiv);
1372 assert_eq!(
1373 tab2.bytes(t.get_string_literal()),
1374 b"\xed\xa0\xbd\xed\xb8\x80"
1375 ); }
1377
1378 fn consume_escape_for_test(src: &str) -> Option<u32> {
1382 let mut sm = SourceErrorManager::new();
1383 let id = sm.add_buffer("t", src);
1384 let tab = AtomTable::new();
1385 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1386 let cp = lex.consume_unicode_escape();
1387 if lex.sm.error_count() != 0 {
1388 None
1389 } else {
1390 Some(cp)
1391 }
1392 }
1393
1394 #[test]
1395 fn unicode_escape_4hex_and_braced() {
1396 assert_eq!(consume_escape_for_test("\\u0041"), Some(0x41)); assert_eq!(consume_escape_for_test("\\u{1F600}"), Some(0x1F600));
1398 assert_eq!(consume_escape_for_test("\\u{}"), None); assert_eq!(consume_escape_for_test("\\uXY"), None); }
1401
1402 fn kinds(src: &str) -> Vec<TokenKind> {
1403 let mut sm = SourceErrorManager::new();
1404 let id = sm.add_buffer("t", src);
1405 let tab = AtomTable::new();
1406 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1407 let mut out = vec![];
1408 loop {
1409 let k = lex.advance(GrammarContext::AllowDiv).kind();
1410 out.push(k);
1411 if k == TokenKind::eof {
1412 break;
1413 }
1414 }
1415 out
1416 }
1417
1418 fn kinds_ctx(src: &str, ctx: GrammarContext) -> Vec<TokenKind> {
1421 let mut sm = SourceErrorManager::new();
1422 let id = sm.add_buffer("t", src);
1423 let tab = AtomTable::new();
1424 let mut lex = JSLexer::new(id, &mut sm, &tab, ctx);
1425 let mut out = vec![];
1426 loop {
1427 let k = lex.advance(ctx).kind();
1428 out.push(k);
1429 if k == TokenKind::eof {
1430 break;
1431 }
1432 }
1433 out
1434 }
1435
1436 #[test]
1437 fn flow_type_context() {
1438 use TokenKind::*;
1439 assert_eq!(kinds_ctx("{|", GrammarContext::Type), vec![l_bracepipe, eof]);
1440 assert_eq!(kinds_ctx("|}", GrammarContext::Type), vec![piper_brace, eof]);
1441 assert_eq!(
1443 kinds_ctx("{ }", GrammarContext::Type),
1444 vec![l_brace, r_brace, eof]
1445 );
1446 assert_eq!(kinds_ctx("<", GrammarContext::Type), vec![less, eof]);
1448 assert_eq!(
1450 kinds_ctx(">>", GrammarContext::Type),
1451 vec![greater, greater, eof]
1452 );
1453 assert_eq!(
1455 kinds_ctx("??", GrammarContext::Type),
1456 vec![question, question, eof]
1457 );
1458 assert_eq!(kinds_ctx("%checks", GrammarContext::Type), vec![identifier, eof]);
1460 assert_eq!(kinds_ctx("@foo", GrammarContext::Type), vec![identifier, eof]);
1462 assert_eq!(
1464 kinds_ctx("{|", GrammarContext::AllowDiv),
1465 vec![l_brace, pipe, eof]
1466 );
1467 assert_eq!(
1468 kinds_ctx("@foo", GrammarContext::AllowDiv),
1469 vec![at, identifier, eof]
1470 );
1471 }
1472
1473 fn kinds_nonstrict(src: &str) -> Vec<TokenKind> {
1475 let mut sm = SourceErrorManager::new();
1476 let id = sm.add_buffer("t", src);
1477 let tab = AtomTable::new();
1478 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1479 lex.set_strict_mode(false);
1480 let mut out = vec![];
1481 loop {
1482 let k = lex.advance(GrammarContext::AllowDiv).kind();
1483 out.push(k);
1484 if k == TokenKind::eof {
1485 break;
1486 }
1487 }
1488 out
1489 }
1490
1491 fn ident_bytes(src: &str) -> Vec<u8> {
1493 let mut sm = SourceErrorManager::new();
1494 let id = sm.add_buffer("t", src);
1495 let tab = AtomTable::new();
1496 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1497 let tok = lex.advance(GrammarContext::AllowDiv);
1498 assert_eq!(tok.kind(), TokenKind::identifier);
1499 let ab = tok.get_identifier();
1500 tab.bytes(ab).to_vec()
1501 }
1502
1503 #[test]
1504 fn identifiers_and_reswords() {
1505 use TokenKind::*;
1506 assert_eq!(kinds("foo _bar $x9"), vec![identifier, identifier, identifier, eof]);
1507 assert_eq!(
1508 kinds("function for yield"),
1509 vec![rw_function, rw_for, rw_yield, eof]
1510 ); assert_eq!(kinds_nonstrict("yield"), vec![identifier, eof]);
1513 assert_eq!(
1515 kinds_nonstrict("implements interface package private protected public static"),
1516 vec![
1517 identifier, identifier, identifier, identifier, identifier, identifier,
1518 identifier, eof
1519 ]
1520 );
1521 assert_eq!(kinds("\u{00e9}tude"), vec![identifier, eof]); assert_eq!(kinds("\\u0041bc"), vec![identifier, eof]); assert_eq!(ident_bytes("caf\u{00e9}"), b"caf\xc3\xa9");
1527 assert_eq!(ident_bytes("\\u0041bc"), b"Abc");
1529 }
1530
1531 fn num_bits(src: &str) -> u64 {
1533 let mut sm = SourceErrorManager::new();
1534 let id = sm.add_buffer("t", src);
1535 let tab = AtomTable::new();
1536 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1537 let tok = lex.advance(GrammarContext::AllowDiv);
1538 assert_eq!(tok.kind(), TokenKind::numeric_literal, "src={src:?}");
1539 tok.get_numeric_literal().to_bits()
1540 }
1541
1542 fn bigint_bytes(src: &str) -> (Vec<u8>, Vec<u8>) {
1544 let mut sm = SourceErrorManager::new();
1545 let id = sm.add_buffer("t", src);
1546 let tab = AtomTable::new();
1547 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1548 let tok = lex.advance(GrammarContext::AllowDiv);
1549 assert_eq!(tok.kind(), TokenKind::bigint_literal, "src={src:?}");
1550 let v = tab.bytes(tok.get_bigint_literal()).to_vec();
1551 let r = tab.bytes(tok.get_bigint_literal_raw_value()).to_vec();
1552 (v, r)
1553 }
1554
1555 #[test]
1556 fn numbers_basic() {
1557 use TokenKind::*;
1558 assert_eq!(num_bits("5"), 5.0f64.to_bits());
1559 assert_eq!(num_bits("0.1"), 0.1f64.to_bits());
1560 assert_eq!(num_bits("0xff"), 255.0f64.to_bits());
1561 assert_eq!(num_bits("0o17"), 15.0f64.to_bits());
1562 assert_eq!(num_bits("0b1010"), 10.0f64.to_bits());
1563 assert_eq!(num_bits("1e10"), 1e10f64.to_bits());
1564 assert_eq!(num_bits("1_000"), 1000.0f64.to_bits());
1565 assert_eq!(num_bits(".5"), 0.5f64.to_bits());
1566 assert_eq!(num_bits("3.14e2"), 314.0f64.to_bits());
1567 assert_eq!(num_bits("0XAB"), (0xab as f64).to_bits());
1568 assert_eq!(num_bits("0o7"), 7.0f64.to_bits());
1569 assert_eq!(num_bits("0b11"), 3.0f64.to_bits());
1570 assert_eq!(num_bits("2E-3"), 2e-3f64.to_bits());
1571 assert_eq!(
1573 kinds("5 0xff 1.5"),
1574 vec![numeric_literal, numeric_literal, numeric_literal, eof]
1575 );
1576 }
1577
1578 #[test]
1579 fn bigint_basic() {
1580 assert_eq!(bigint_bytes("10n"), (b"10".to_vec(), b"10n".to_vec()));
1581 assert_eq!(bigint_bytes("0xffn"), (b"0xff".to_vec(), b"0xffn".to_vec()));
1582 assert_eq!(bigint_bytes("255n"), (b"255".to_vec(), b"255n".to_vec()));
1583 assert_eq!(bigint_bytes("0n"), (b"0".to_vec(), b"0n".to_vec()));
1584 assert_eq!(
1586 bigint_bytes("1_000n"),
1587 (b"1000".to_vec(), b"1_000n".to_vec())
1588 );
1589 }
1590
1591 fn str_cooked(src: &str) -> (Vec<u8>, bool) {
1594 let mut sm = SourceErrorManager::new();
1595 let id = sm.add_buffer("t", src);
1596 let tab = AtomTable::new();
1597 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1598 let tok = lex.advance(GrammarContext::AllowDiv);
1599 assert_eq!(tok.kind(), TokenKind::string_literal, "src={src:?}");
1600 let cooked = tab.bytes(tok.get_string_literal()).to_vec();
1601 let escapes = tok.get_string_literal_contains_escapes();
1602 (cooked, escapes)
1603 }
1604
1605 #[test]
1606 fn strings_basic() {
1607 use TokenKind::*;
1608 assert_eq!(kinds("'a' \"b\""), vec![string_literal, string_literal, eof]);
1609 assert_eq!(str_cooked("'hello'"), (b"hello".to_vec(), false));
1610 assert_eq!(str_cooked("\"a\\tb\""), (b"a\tb".to_vec(), true)); assert_eq!(str_cooked("'\\n\\r\\\\'"), (vec![10, 13, b'\\'], true));
1612 assert_eq!(str_cooked("'\\x41'"), (b"A".to_vec(), true)); assert_eq!(str_cooked("'\\u00e9'"), (b"\xc3\xa9".to_vec(), true)); assert_eq!(str_cooked("'a\\\nb'"), (b"ab".to_vec(), true)); assert_eq!(str_cooked("'caf\u{00e9}'"), (b"caf\xc3\xa9".to_vec(), false)); }
1617
1618 fn private_ident_bytes(src: &str) -> Vec<u8> {
1620 let mut sm = SourceErrorManager::new();
1621 let id = sm.add_buffer("t", src);
1622 let tab = AtomTable::new();
1623 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1624 let tok = lex.advance(GrammarContext::AllowDiv);
1625 assert_eq!(tok.kind(), TokenKind::private_identifier);
1626 tab.bytes(tok.get_private_identifier()).to_vec()
1627 }
1628
1629 fn template(src: &str) -> (TokenKind, Option<Vec<u8>>, Vec<u8>) {
1632 let mut sm = SourceErrorManager::new();
1633 let id = sm.add_buffer("t", src);
1634 let tab = AtomTable::new();
1635 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1636 let tok = lex.advance(GrammarContext::AllowDiv);
1637 assert!(tok.is_template_literal(), "src={src:?} kind={:?}", tok.kind());
1638 let kind = tok.kind();
1639 let cooked = tok.get_template_value().map(|a| tab.bytes(a).to_vec());
1640 let raw = tab.bytes(tok.get_template_raw_value()).to_vec();
1641 (kind, cooked, raw)
1642 }
1643
1644 #[test]
1645 fn templates_basic() {
1646 use TokenKind::*;
1647 assert_eq!(
1649 template("`abc`"),
1650 (no_substitution_template, Some(b"abc".to_vec()), b"abc".to_vec())
1651 );
1652 assert_eq!(template("`a${").0, template_head);
1654 assert_eq!(
1656 template("`a\\nb`"),
1657 (no_substitution_template, Some(vec![b'a', 10, b'b']), b"a\\nb".to_vec())
1658 );
1659 assert_eq!(
1661 template("`\\9`"),
1662 (no_substitution_template, None, b"\\9".to_vec())
1663 );
1664 assert_eq!(
1666 template("`a\rb`"),
1667 (no_substitution_template, Some(vec![b'a', 10, b'b']), vec![b'a', 10, b'b'])
1668 );
1669 assert_eq!(
1672 kinds("`a${b}")[..3].to_vec(),
1673 vec![template_head, identifier, r_brace]
1674 );
1675 }
1676
1677 #[test]
1678 fn private_identifiers() {
1679 use TokenKind::*;
1680 assert_eq!(
1681 kinds("#foo #bar"),
1682 vec![private_identifier, private_identifier, eof]
1683 );
1684 assert_eq!(private_ident_bytes("#x"), b"x"); assert_eq!(kinds("#"), vec![eof]);
1688 }
1689
1690 #[test]
1691 fn punctuators_and_comments() {
1692 use TokenKind::*;
1693 assert_eq!(
1694 kinds("{ } ( ) ;"),
1695 vec![l_brace, r_brace, l_paren, r_paren, semi, eof]
1696 );
1697 assert_eq!(kinds("; /* c */ ;"), vec![semi, semi, eof]);
1699 assert_eq!(kinds("; // line\n;"), vec![semi, semi, eof]);
1700 }
1701
1702 #[test]
1703 fn token_storage() {
1704 let mut sm = SourceErrorManager::new();
1709 let id = sm.add_buffer("t", "a + b");
1710 let tab = AtomTable::new();
1711 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1712 lex.set_store_tokens(true);
1713 assert!(lex.get_store_tokens());
1714 while lex.advance(GrammarContext::AllowDiv).kind() != TokenKind::eof {}
1715 let toks: Vec<TokenKind> =
1716 lex.get_stored_tokens().iter().map(|t| t.kind()).collect();
1717 assert_eq!(
1718 toks,
1719 vec![
1720 TokenKind::identifier,
1721 TokenKind::plus,
1722 TokenKind::identifier,
1723 TokenKind::eof
1724 ]
1725 );
1726 }
1727
1728 #[test]
1729 fn comment_storage() {
1730 let mut sm = SourceErrorManager::new();
1731 let id = sm.add_buffer("t", "a /*c*/ // line\nb");
1732 let tab = AtomTable::new();
1733 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1734 lex.set_store_comments(true);
1735 while lex.advance(GrammarContext::AllowDiv).kind() != TokenKind::eof {}
1736 let cs: Vec<(CommentKind, u32, u32)> = lex
1738 .get_stored_comments()
1739 .iter()
1740 .map(|c| {
1741 let r = c.source_range();
1742 (c.kind(), r.start.offset, r.end.offset)
1743 })
1744 .collect();
1745 assert_eq!(cs.len(), 2);
1746 assert_eq!(cs[0].0, CommentKind::Block);
1747 assert_eq!(cs[1].0, CommentKind::Line);
1748 let buf = sm.source_buffer(id);
1750 let raw = buf.raw();
1751 assert_eq!(&raw[cs[0].1 as usize..cs[0].2 as usize], b"/*c*/");
1752 assert_eq!(&raw[cs[1].1 as usize..cs[1].2 as usize], b"// line");
1753 }
1754
1755 #[test]
1756 fn magic_comments() {
1757 let mut sm = SourceErrorManager::new();
1758 let id = sm.add_buffer(
1759 "t",
1760 "a\n//# sourceURL=http://x/y.js\n//# sourceMappingURL=z.map\nb",
1761 );
1762 let tab = AtomTable::new();
1763 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1764 while lex.advance(GrammarContext::AllowDiv).kind() != TokenKind::eof {}
1765 assert_eq!(lex.get_source_url(), Some("http://x/y.js"));
1766 assert_eq!(lex.get_source_mapping_url(), Some("z.map"));
1767 }
1768
1769 #[test]
1770 fn save_point_restore() {
1771 let mut sm = SourceErrorManager::new();
1772 let id = sm.add_buffer("t", "a . b");
1773 let tab = AtomTable::new();
1774 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1775 lex.advance(GrammarContext::AllowDiv); let sp = lex.save_point();
1777 lex.advance(GrammarContext::AllowDiv); lex.advance(GrammarContext::AllowDiv); sp.restore(&mut lex);
1780 assert_eq!(lex.token().kind(), TokenKind::identifier);
1782 assert_eq!(
1783 lex.advance(GrammarContext::AllowDiv).kind(),
1784 TokenKind::period
1785 );
1786 }
1787
1788 #[test]
1789 fn save_point_truncates_storage() {
1790 let mut sm = SourceErrorManager::new();
1792 let id = sm.add_buffer("t", "a /*c*/ . b");
1793 let tab = AtomTable::new();
1794 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1795 lex.set_store_tokens(true);
1796 lex.set_store_comments(true);
1797 lex.advance(GrammarContext::AllowDiv); let toks_before = lex.get_stored_tokens().len();
1799 let comments_before = lex.get_stored_comments().len();
1800 let sp = lex.save_point();
1801 lex.advance(GrammarContext::AllowDiv); lex.advance(GrammarContext::AllowDiv); assert!(lex.get_stored_tokens().len() > toks_before);
1804 assert!(lex.get_stored_comments().len() > comments_before);
1805 sp.restore(&mut lex);
1806 assert_eq!(lex.get_stored_tokens().len(), toks_before);
1807 assert_eq!(lex.get_stored_comments().len(), comments_before);
1808 }
1809
1810 #[test]
1811 fn is_directive() {
1812 fn directive(src: &str) -> bool {
1813 let mut sm = SourceErrorManager::new();
1814 let id = sm.add_buffer("t", src);
1815 let tab = AtomTable::new();
1816 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1817 lex.advance(GrammarContext::AllowDiv); lex.is_current_token_a_directive()
1819 }
1820 assert!(directive("\"use strict\";"));
1821 assert!(directive("\"use strict\"\n"));
1822 assert!(directive("\"x\" /*c*/ ;"));
1823 assert!(directive("\"x\"")); assert!(directive("\"x\" // line")); assert!(directive("\"x\" }")); assert!(!directive("\"x\" + y")); assert!(!directive("foo")); }
1829
1830 #[test]
1831 fn is_directive_does_not_corrupt() {
1832 let mut sm = SourceErrorManager::new();
1834 let id = sm.add_buffer("t", "\"x\" /*c*/ + y");
1835 let tab = AtomTable::new();
1836 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1837 assert_eq!(
1838 lex.advance(GrammarContext::AllowDiv).kind(),
1839 TokenKind::string_literal
1840 );
1841 assert!(!lex.is_current_token_a_directive());
1842 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), TokenKind::plus);
1844 assert_eq!(
1845 lex.advance(GrammarContext::AllowDiv).kind(),
1846 TokenKind::identifier
1847 );
1848 }
1849
1850 #[test]
1851 fn rescan_rbrace_template() {
1852 use TokenKind::*;
1853 let mut sm = SourceErrorManager::new();
1856 let id = sm.add_buffer("t", "`a${b}c`");
1857 let tab = AtomTable::new();
1858 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1859 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), template_head);
1860 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), identifier);
1861 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), r_brace);
1862 let tok = lex.rescan_rbrace_in_template_literal();
1863 assert_eq!(tok.kind(), template_tail);
1864 let cooked = tok.get_template_value().map(|a| tab.bytes(a).to_vec());
1865 assert_eq!(cooked, Some(b"c".to_vec()));
1866 }
1867
1868 #[test]
1869 fn rescan_rbrace_template_middle() {
1870 use TokenKind::*;
1871 let mut sm = SourceErrorManager::new();
1873 let id = sm.add_buffer("t", "`a${b}c${d}e`");
1874 let tab = AtomTable::new();
1875 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1876 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), template_head);
1877 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), identifier);
1878 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), r_brace);
1879 assert_eq!(
1880 lex.rescan_rbrace_in_template_literal().kind(),
1881 template_middle
1882 );
1883 }
1884
1885 #[test]
1886 fn newline_flag_tracks_line_terminators() {
1887 let mut sm = SourceErrorManager::new();
1888 let id = sm.add_buffer("t", ";\n;");
1889 let tab = AtomTable::new();
1890 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1891 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), TokenKind::semi);
1893 assert!(!lex.is_new_line_before_current_token());
1894 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), TokenKind::semi);
1896 assert!(lex.is_new_line_before_current_token());
1897 }
1898
1899 #[test]
1904 fn unicode_only_id_start_via_c2_arm() {
1905 use TokenKind::*;
1906 assert_eq!(kinds("\u{00aa}"), vec![identifier, eof]);
1907 }
1908
1909 #[test]
1910 fn check_following_character() {
1911 let mut sm = SourceErrorManager::new();
1912 let id = sm.add_buffer("t", "renders?");
1913 let tab = AtomTable::new();
1914 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1915 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), TokenKind::identifier);
1917 assert!(lex.check_following_character(b'?'));
1918 assert!(!lex.check_following_character(b':'));
1919 }
1920
1921 #[test]
1922 fn token_input_str_returns_source_text() {
1923 let mut sm = SourceErrorManager::new();
1924 let id = sm.add_buffer("t", " foobar ");
1925 let tab = AtomTable::new();
1926 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1927 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), TokenKind::identifier);
1928 assert_eq!(lex.token_input_str(), b"foobar");
1929 }
1930
1931 #[test]
1932 fn convert_cur_token_to_ident_op_for_as() {
1933 use crate::token_kinds::token_kind_str;
1934 let mut sm = SourceErrorManager::new();
1935 let id = sm.add_buffer("t", "as");
1936 let tab = AtomTable::new();
1937 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1938 assert_eq!(lex.advance(GrammarContext::AllowDiv).kind(), TokenKind::identifier);
1940 assert_eq!(token_kind_str(TokenKind::as_operator), "as");
1942 lex.convert_cur_token_to_ident_op(TokenKind::as_operator);
1943 assert_eq!(lex.token().kind(), TokenKind::as_operator);
1944 }
1945
1946 #[test]
1947 fn get_identifier_and_buffer_id() {
1948 let mut sm = SourceErrorManager::new();
1949 let id = sm.add_buffer("t", "abc");
1950 let tab = AtomTable::new();
1951 let lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1952 let a = lex.get_identifier(b"hello");
1954 assert_eq!(tab.bytes(a), b"hello");
1955 assert_eq!(lex.get_buffer_id(), id);
1957 assert_eq!(lex.buffer_bytes(), b"abc");
1959 assert_eq!(lex.get_buffer_start(), 0);
1960 assert_eq!(lex.get_buffer_end(), 3);
1961 }
1962
1963 #[test]
1964 fn source_mgr_mut_reports_errors() {
1965 let mut sm = SourceErrorManager::new();
1968 let id = sm.add_buffer("t", "x");
1969 let tab = AtomTable::new();
1970 let mut lex = JSLexer::new(id, &mut sm, &tab, GrammarContext::AllowDiv);
1971 let loc = lex.token().start_loc();
1972 lex.get_source_mgr_mut()
1973 .error_at(loc, None, "boom", Subsystem::Parser);
1974 assert_eq!(lex.get_source_mgr().error_count(), 1);
1975 }
1976}