use hermes_atom_table::AtomBytes;
use hermes_support::location::{SMLoc, SMRange, SourceId};
use crate::token_kinds::TokenKind;
#[derive(Copy, Clone, Debug)]
pub struct RegExpLiteral {
body: AtomBytes,
flags: AtomBytes,
}
impl RegExpLiteral {
pub fn new(body: AtomBytes, flags: AtomBytes) -> RegExpLiteral {
RegExpLiteral { body, flags }
}
pub fn body(&self) -> AtomBytes {
self.body
}
pub fn flags(&self) -> AtomBytes {
self.flags
}
}
#[derive(Clone, Debug)]
pub struct Token {
kind: TokenKind,
range: SMRange,
numeric: f64,
ident: Option<AtomBytes>,
string_literal: Option<AtomBytes>,
regexp: Option<RegExpLiteral>,
raw_string: Option<AtomBytes>,
string_literal_contains_escapes: bool,
}
impl Token {
pub fn new(source: SourceId) -> Token {
let loc = SMLoc { source, offset: 0 };
Token {
kind: TokenKind::none,
range: SMRange {
start: loc,
end: loc,
},
numeric: 0.0,
ident: None,
string_literal: None,
regexp: None,
raw_string: None,
string_literal_contains_escapes: false,
}
}
pub fn kind(&self) -> TokenKind {
self.kind
}
pub fn is_res_word(&self) -> bool {
self.kind.is_res_word()
}
pub fn is_template_literal(&self) -> bool {
matches!(
self.kind,
TokenKind::no_substitution_template
| TokenKind::template_head
| TokenKind::template_middle
| TokenKind::template_tail
)
}
pub fn start_loc(&self) -> SMLoc {
self.range.start
}
pub fn end_loc(&self) -> SMLoc {
self.range.end
}
pub fn source_range(&self) -> SMRange {
self.range
}
pub fn get_numeric_literal(&self) -> f64 {
debug_assert_eq!(self.kind, TokenKind::numeric_literal);
self.numeric
}
pub fn get_identifier(&self) -> AtomBytes {
debug_assert_eq!(self.kind, TokenKind::identifier);
self.ident.unwrap()
}
pub fn get_private_identifier(&self) -> AtomBytes {
debug_assert_eq!(self.kind, TokenKind::private_identifier);
self.ident.unwrap()
}
pub fn get_res_word_identifier(&self) -> AtomBytes {
debug_assert!(self.is_res_word());
self.ident.unwrap()
}
pub fn get_res_word_or_identifier(&self) -> AtomBytes {
debug_assert!(self.kind == TokenKind::identifier || self.is_res_word());
self.ident.unwrap()
}
pub fn get_string_literal(&self) -> AtomBytes {
debug_assert_eq!(self.kind, TokenKind::string_literal);
self.string_literal.unwrap()
}
pub fn get_string_literal_raw_value(&self) -> AtomBytes {
debug_assert_eq!(self.kind, TokenKind::string_literal);
self.raw_string.unwrap()
}
pub fn get_string_literal_contains_escapes(&self) -> bool {
debug_assert_eq!(self.kind, TokenKind::string_literal);
self.string_literal_contains_escapes
}
pub fn get_template_literal_contains_not_escapes(&self) -> bool {
debug_assert!(self.is_template_literal());
self.string_literal.is_none()
}
pub fn get_template_value(&self) -> Option<AtomBytes> {
debug_assert!(self.is_template_literal());
self.string_literal
}
pub fn get_template_raw_value(&self) -> AtomBytes {
debug_assert!(self.is_template_literal());
self.raw_string.unwrap()
}
pub fn get_bigint_literal(&self) -> AtomBytes {
debug_assert_eq!(self.kind, TokenKind::bigint_literal);
self.string_literal.unwrap()
}
pub fn get_bigint_literal_raw_value(&self) -> AtomBytes {
debug_assert_eq!(self.kind, TokenKind::bigint_literal);
self.raw_string.unwrap()
}
pub fn get_regexp_literal(&self) -> RegExpLiteral {
debug_assert_eq!(self.kind, TokenKind::regexp_literal);
self.regexp.unwrap()
}
pub fn get_jsx_text_value(&self) -> AtomBytes {
debug_assert_eq!(self.kind, TokenKind::jsx_text);
self.string_literal.unwrap()
}
pub fn get_jsx_text_raw(&self) -> AtomBytes {
debug_assert_eq!(self.kind, TokenKind::jsx_text);
self.raw_string.unwrap()
}
pub(crate) fn set_start(&mut self, start: SMLoc) {
self.range.start = start;
}
pub(crate) fn set_end(&mut self, end: SMLoc) {
self.range.end = end;
}
pub(crate) fn set_range(&mut self, range: SMRange) {
self.range = range;
}
pub(crate) fn set_punctuator(&mut self, kind: TokenKind) {
self.kind = kind;
}
pub(crate) fn set_ident_op(&mut self, kind: TokenKind) {
self.kind = kind;
}
pub(crate) fn set_eof(&mut self) {
self.kind = TokenKind::eof;
}
pub(crate) fn set_bigint_literal(&mut self, bigint: AtomBytes, raw: AtomBytes) {
self.kind = TokenKind::bigint_literal;
self.string_literal = Some(bigint);
self.raw_string = Some(raw);
}
pub(crate) fn set_numeric_literal(&mut self, literal: f64) {
self.kind = TokenKind::numeric_literal;
self.numeric = literal;
}
pub(crate) fn set_identifier(&mut self, ident: AtomBytes) {
self.kind = TokenKind::identifier;
self.ident = Some(ident);
}
pub(crate) fn set_private_identifier(&mut self, ident: AtomBytes) {
self.kind = TokenKind::private_identifier;
self.ident = Some(ident);
}
pub(crate) fn set_string_literal(&mut self, literal: AtomBytes, contains_escapes: bool) {
self.kind = TokenKind::string_literal;
self.string_literal = Some(literal);
self.string_literal_contains_escapes = contains_escapes;
}
#[allow(dead_code)]
pub(crate) fn set_jsx_string_literal(&mut self, literal: AtomBytes, raw: AtomBytes) {
self.kind = TokenKind::string_literal;
self.string_literal = Some(literal);
self.raw_string = Some(raw);
self.string_literal_contains_escapes = false;
}
pub(crate) fn set_regexp_literal(&mut self, literal: RegExpLiteral) {
self.kind = TokenKind::regexp_literal;
self.regexp = Some(literal);
}
pub(crate) fn set_res_word(&mut self, kind: TokenKind, ident: AtomBytes) {
debug_assert!(kind.is_res_word());
self.kind = kind;
self.ident = Some(ident);
}
pub(crate) fn set_template_literal(
&mut self,
kind: TokenKind,
cooked: Option<AtomBytes>,
raw: AtomBytes,
) {
debug_assert!(matches!(
kind,
TokenKind::no_substitution_template
| TokenKind::template_head
| TokenKind::template_middle
| TokenKind::template_tail
));
self.kind = kind;
self.string_literal = cooked;
self.raw_string = Some(raw);
}
pub(crate) fn set_jsx_text(&mut self, value: AtomBytes, raw: AtomBytes) {
self.kind = TokenKind::jsx_text;
self.string_literal = Some(value);
self.raw_string = Some(raw);
}
}
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum CommentKind {
Line,
Block,
Hashbang,
}
#[derive(Copy, Clone, Debug)]
pub struct StoredComment {
kind: CommentKind,
range: SMRange,
}
impl StoredComment {
pub fn new(kind: CommentKind, range: SMRange) -> StoredComment {
StoredComment { kind, range }
}
pub fn kind(&self) -> CommentKind {
self.kind
}
pub fn source_range(&self) -> SMRange {
self.range
}
pub fn get_string<'a>(&self, buffer: &'a [u8]) -> &'a [u8] {
let start = self.range.start.offset as usize + 2;
let end = if self.kind == CommentKind::Block {
self.range.end.offset as usize - 2
} else {
self.range.end.offset as usize
};
debug_assert!(end >= start, "invalid comment range");
&buffer[start..end]
}
pub fn get_full_string<'a>(&self, buffer: &'a [u8]) -> &'a [u8] {
&buffer[self.range.start.offset as usize..self.range.end.offset as usize]
}
}
#[derive(Copy, Clone, Debug)]
pub struct StoredToken {
kind: TokenKind,
range: SMRange,
}
impl StoredToken {
pub fn new(kind: TokenKind, range: SMRange) -> StoredToken {
StoredToken { kind, range }
}
pub fn kind(&self) -> TokenKind {
self.kind
}
pub fn source_range(&self) -> SMRange {
self.range
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::token_kinds::TokenKind;
use hermes_support::location::{SMLoc, SMRange, SourceId};
#[test]
fn punctuator_token() {
let id = SourceId::from_index(0);
let mut t = Token::new(id);
t.set_punctuator(TokenKind::l_brace);
t.set_range(SMRange {
start: SMLoc {
source: id,
offset: 0,
},
end: SMLoc {
source: id,
offset: 1,
},
});
assert_eq!(t.kind(), TokenKind::l_brace);
assert_eq!(t.start_loc().offset, 0);
assert_eq!(t.end_loc().offset, 1);
}
#[test]
fn jsx_string_literal_value_and_raw() {
let tab = hermes_atom_table::AtomTable::new();
let value = tab.atom_bytes(b"a<b");
let raw = tab.atom_bytes(b"a<b");
let mut t = Token::new(SourceId::from_index(0));
t.set_jsx_string_literal(value, raw);
assert_eq!(t.kind(), TokenKind::string_literal);
assert_eq!(t.get_string_literal(), value);
assert_eq!(t.get_string_literal_raw_value(), raw);
assert!(!t.get_string_literal_contains_escapes());
}
#[test]
fn template_literal_contains_not_escapes() {
let tab = hermes_atom_table::AtomTable::new();
let raw = tab.atom_bytes(b"\\9");
let mut t = Token::new(SourceId::from_index(0));
t.set_template_literal(TokenKind::no_substitution_template, None, raw);
assert!(t.get_template_literal_contains_not_escapes());
let cooked = tab.atom_bytes(b"ok");
t.set_template_literal(TokenKind::template_head, Some(cooked), raw);
assert!(!t.get_template_literal_contains_not_escapes());
assert_eq!(t.get_template_value(), Some(cooked));
}
#[test]
fn stored_comment_get_string() {
let id = SourceId::from_index(0);
let loc = |off| SMLoc {
source: id,
offset: off,
};
let buffer = b"// hello /* world */ rest";
let line = StoredComment::new(
CommentKind::Line,
SMRange {
start: loc(0),
end: loc(8),
},
);
assert_eq!(line.get_string(buffer), b" hello");
assert_eq!(line.get_full_string(buffer), b"// hello");
let block = StoredComment::new(
CommentKind::Block,
SMRange {
start: loc(9),
end: loc(20),
},
);
assert_eq!(block.get_string(buffer), b" world ");
assert_eq!(block.get_full_string(buffer), b"/* world */");
}
}