use super::{input::Input, LexResult, Lexer};
use error::{ErrorToDiag, SyntaxError};
use swc_common::{BytePos, Span};
use unicode_xid::UnicodeXID;
pub(super) struct Raw(pub Option<String>);
impl Raw {
#[inline]
pub fn push_str(&mut self, s: &str) {
match self.0 {
Some(ref mut st) => st.push_str(s),
_ => {}
}
}
#[inline]
pub fn push(&mut self, c: char) {
match self.0 {
Some(ref mut st) => st.push(c),
_ => {}
}
}
}
impl<'a, I: Input> Lexer<'a, I> {
pub(super) fn span(&self, start: BytePos) -> Span {
let end = self.last_pos();
if cfg!(debug_assertions) && start > end {
unreachable!(
"assertion failed: (span.start <= span.end).
start = {}, end = {}",
start.0, end.0
)
}
Span::new(start, end, Default::default())
}
pub(super) fn bump(&mut self) {
self.input.bump()
}
pub(super) fn is(&mut self, c: char) -> bool {
self.cur() == Some(c)
}
pub(super) fn eat(&mut self, c: char) -> bool {
if self.is(c) {
self.bump();
true
} else {
false
}
}
pub(super) fn cur(&mut self) -> Option<char> {
self.input.current()
}
pub(super) fn peek(&mut self) -> Option<char> {
self.input.peek()
}
pub(super) fn peek_ahead(&mut self) -> Option<char> {
self.input.peek_ahead()
}
pub(super) fn cur_pos(&mut self) -> BytePos {
self.input.cur_pos()
}
pub(super) fn last_pos(&self) -> BytePos {
self.input.last_pos()
}
pub(super) fn error(&mut self, start: BytePos, kind: SyntaxError) -> LexResult<!> {
let span = self.span(start);
self.error_span(span, kind)
}
pub(super) fn error_span(&mut self, span: Span, kind: SyntaxError) -> LexResult<!> {
let err = ErrorToDiag {
handler: self.session.handler,
span,
error: kind,
};
Err(err)?
}
pub(super) fn skip_space(&mut self) -> LexResult<()> {
let mut line_break = false;
while let Some(c) = self.cur() {
match c {
_ if c.is_ws() => {}
_ if c.is_line_break() => {
self.state.had_line_break = true;
}
'/' => {
if self.peek() == Some('/') {
self.skip_line_comment(2);
continue;
} else if self.peek() == Some('*') {
self.skip_block_comment()?;
continue;
}
break;
}
_ => break,
}
self.bump();
}
Ok(())
}
pub(super) fn skip_line_comment(&mut self, start_skip: usize) {
let start = self.cur_pos();
for _ in 0..start_skip {
self.bump();
}
while let Some(c) = self.cur() {
self.bump();
if c.is_line_break() {
self.state.had_line_break = true;
}
match c {
'\n' | '\r' | '\u{2028}' | '\u{2029}' => {
break;
}
_ => {}
}
}
}
pub(super) fn skip_block_comment(&mut self) -> LexResult<()> {
let start = self.cur_pos();
debug_assert_eq!(self.cur(), Some('/'));
debug_assert_eq!(self.peek(), Some('*'));
self.bump();
self.bump();
let mut was_star = false;
while let Some(c) = self.cur() {
if was_star && self.eat('/') {
return Ok(());
}
if c.is_line_break() {
self.state.had_line_break = true;
}
was_star = self.is('*');
self.bump();
}
self.error(start, SyntaxError::UnterminatedBlockComment)?
}
}
pub trait CharExt: Copy {
fn to_char(self) -> Option<char>;
fn is_ident_start(self) -> bool {
let c = match self.to_char() {
Some(c) => c,
None => return false,
};
c == '$' || c == '_' || UnicodeXID::is_xid_start(c)
}
fn is_ident_part(self) -> bool {
let c = match self.to_char() {
Some(c) => c,
None => return false,
};
c == '$' || c == '_' || c == '\u{200c}' || c == '\u{200d}' || UnicodeXID::is_xid_continue(c)
}
fn is_line_break(self) -> bool {
let c = match self.to_char() {
Some(c) => c,
None => return false,
};
match c {
'\r' | '\n' | '\u{2028}' | '\u{2029}' => true,
_ => false,
}
}
fn is_ws(self) -> bool {
let c = match self.to_char() {
Some(c) => c,
None => return false,
};
match c {
'\u{0009}' | '\u{000b}' | '\u{000c}' | '\u{0020}' | '\u{00a0}' | '\u{feff}' => true,
'\u{1680}'
| '\u{180e}'
| '\u{2000}'...'\u{200a}'
| '\u{202f}'
| '\u{205f}'
| '\u{3000}' => {
true
}
_ => false,
}
}
}
impl CharExt for char {
#[inline(always)]
fn to_char(self) -> Option<char> {
Some(self)
}
}