use crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer};
use crate::source::ReadChar;
use boa_ast::{PositionGroup, Punctuator};
use boa_interner::Interner;
const CHAR_ASSIGN: u32 = '=' as u32;
macro_rules! vop {
($cursor:ident, $assign_op:expr, $op:expr) => ({
match $cursor.peek_char()? {
None => $op,
Some(CHAR_ASSIGN) => {
$cursor.next_char()?.expect("= token vanished");
$assign_op
}
Some(_) => $op,
}
});
($cursor:ident, $assign_op:expr, $op:expr, {$($case:pat => $block:expr), +}) => ({
match $cursor.peek_char()? {
None => $op,
Some(CHAR_ASSIGN) => {
$cursor.next_char()?.expect("= token vanished");
$assign_op
},
$($case => {
$cursor.next_char()?.expect("Token vanished");
$block
})+,
Some(_) => $op,
}
});
}
macro_rules! op {
($cursor:ident, $start_pos:expr, $assign_op:expr, $op:expr) => ({
Token::new_by_position_group(
vop!($cursor, $assign_op, $op).into(),
$start_pos, $cursor.pos_group(),
)
});
($cursor:ident, $start_pos:expr, $assign_op:expr, $op:expr, {$($case:pat => $block:expr),+}) => ({
let punc: Punctuator = vop!($cursor, $assign_op, $op, {$($case => $block),+});
Token::new_by_position_group(
punc.into(),
$start_pos, $cursor.pos_group(),
)
});
}
#[derive(Debug, Clone, Copy)]
pub(super) struct Operator {
init: u8,
}
impl Operator {
pub(super) const fn new(init: u8) -> Self {
Self { init }
}
}
impl<R> Tokenizer<R> for Operator {
fn lex(
&mut self,
cursor: &mut Cursor<R>,
start_pos: PositionGroup,
_interner: &mut Interner,
) -> Result<Token, Error>
where
R: ReadChar,
{
Ok(match self.init {
b'*' => op!(cursor, start_pos, Punctuator::AssignMul, Punctuator::Mul, {
Some(0x2A ) => vop!(cursor, Punctuator::AssignPow, Punctuator::Exp)
}),
b'+' => op!(cursor, start_pos, Punctuator::AssignAdd, Punctuator::Add, {
Some(0x2B ) => Punctuator::Inc
}),
b'-' => op!(cursor, start_pos, Punctuator::AssignSub, Punctuator::Sub, {
Some(0x2D ) => Punctuator::Dec
}),
b'%' => op!(cursor, start_pos, Punctuator::AssignMod, Punctuator::Mod),
b'|' => op!(cursor, start_pos, Punctuator::AssignOr, Punctuator::Or, {
Some(0x7C ) => vop!(cursor, Punctuator::AssignBoolOr, Punctuator::BoolOr)
}),
b'&' => op!(cursor, start_pos, Punctuator::AssignAnd, Punctuator::And, {
Some(0x26 ) => vop!(cursor, Punctuator::AssignBoolAnd, Punctuator::BoolAnd)
}),
b'?' => {
let (first, second) = (cursor.peek_char()?, cursor.peek_n(2)?[1]);
match first {
Some(0x3F ) => {
cursor.next_char()?.expect("? vanished");
op!(
cursor,
start_pos,
Punctuator::AssignCoalesce,
Punctuator::Coalesce
)
}
Some(0x2E ) if !matches!(second, Some(second) if (0x30..=0x39 ).contains(&second)) =>
{
cursor.next_char()?.expect(". vanished");
Token::new_by_position_group(
TokenKind::Punctuator(Punctuator::Optional),
start_pos,
cursor.pos_group(),
)
}
_ => Token::new_by_position_group(
TokenKind::Punctuator(Punctuator::Question),
start_pos,
cursor.pos_group(),
),
}
}
b'^' => op!(cursor, start_pos, Punctuator::AssignXor, Punctuator::Xor),
b'=' => op!(cursor, start_pos, if cursor.next_if(0x3D )? {
Punctuator::StrictEq
} else {
Punctuator::Eq
}, Punctuator::Assign, {
Some(0x3E ) => {
Punctuator::Arrow
}
}),
b'<' => {
op!(cursor, start_pos, Punctuator::LessThanOrEq, Punctuator::LessThan, {
Some(0x3C ) => vop!(cursor, Punctuator::AssignLeftSh, Punctuator::LeftSh)
})
}
b'>' => {
op!(cursor, start_pos, Punctuator::GreaterThanOrEq, Punctuator::GreaterThan, {
Some(0x3E ) => vop!(cursor, Punctuator::AssignRightSh, Punctuator::RightSh, {
Some(0x3E ) => vop!(cursor, Punctuator::AssignURightSh, Punctuator::URightSh)
})
})
}
b'!' => op!(
cursor,
start_pos,
vop!(cursor, Punctuator::StrictNotEq, Punctuator::NotEq),
Punctuator::Not
),
b'~' => {
Token::new_by_position_group(Punctuator::Neg.into(), start_pos, cursor.pos_group())
}
op => unimplemented!("operator {}", op),
})
}
}