Rust编写的JavaScript引擎,该项目是一个试验性质的项目。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

98 lines
3.6 KiB

//! This module implements lexing for template literals used in the JavaScript programing language.
use super::{Cursor, Error, Tokenizer};
use crate::{
profiler::BoaProfiler,
syntax::lexer::string::{unescape_string, StringTerminator},
syntax::{
ast::{Position, Span},
lexer::{Token, TokenKind},
},
};
use std::convert::TryFrom;
use std::io::{self, ErrorKind, Read};
/// Template literal lexing.
///
/// Expects: Initial ` to already be consumed by cursor.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-template-literals
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals
#[derive(Debug, Clone, Copy)]
pub(super) struct TemplateLiteral;
impl<R> Tokenizer<R> for TemplateLiteral {
fn lex(&mut self, cursor: &mut Cursor<R>, start_pos: Position) -> Result<Token, Error>
where
R: Read,
{
let _timer = BoaProfiler::global().start_event("TemplateLiteral", "Lexing");
let mut buf = Vec::new();
loop {
let next_chr = char::try_from(cursor.next_char()?.ok_or_else(|| {
Error::from(io::Error::new(
ErrorKind::UnexpectedEof,
"unterminated template literal",
))
})?)
.unwrap();
match next_chr {
'`' => {
let raw = String::from_utf16_lossy(buf.as_slice());
let (cooked, _) = unescape_string(
&mut Cursor::with_position(raw.as_bytes(), start_pos),
start_pos,
StringTerminator::End,
true,
)?;
return Ok(Token::new(
TokenKind::template_no_substitution(raw, cooked),
Span::new(start_pos, cursor.pos()),
));
}
'$' if cursor.peek()? == Some(b'{') => {
let _ = cursor.next_byte()?;
let raw = String::from_utf16_lossy(buf.as_slice());
let (cooked, _) = unescape_string(
&mut Cursor::with_position(raw.as_bytes(), start_pos),
start_pos,
StringTerminator::End,
true,
)?;
return Ok(Token::new(
TokenKind::template_middle(raw, cooked),
Span::new(start_pos, cursor.pos()),
));
}
'\\' => {
let escape = cursor.peek()?.ok_or_else(|| {
Error::from(io::Error::new(
ErrorKind::UnexpectedEof,
"unterminated escape sequence in literal",
))
})?;
buf.push('\\' as u16);
match escape {
b'`' | b'$' | b'\\' => buf.push(cursor.next_byte()?.unwrap() as u16),
_ => continue,
}
}
next_ch => {
if next_ch.len_utf16() == 1 {
buf.push(next_ch as u16);
} else {
let mut code_point_bytes_buf = [0u16; 2];
let code_point_bytes = next_ch.encode_utf16(&mut code_point_bytes_buf);
buf.extend(code_point_bytes.iter());
}
}
}
}
}
}