1use super::{Cursor, Error, Tokenizer};
4use crate::syntax::lexer::TokenKind;
5use crate::{
6 profiler::BoaProfiler,
7 syntax::{
8 ast::{Position, Punctuator, Span},
9 lexer::Token,
10 },
11};
12use std::io::Read;
13
14macro_rules! vop {
18 ($cursor:ident, $assign_op:expr, $op:expr) => ({
19 match $cursor.peek()? {
20 None => Err(Error::syntax("abrupt end - could not preview next value as part of the operator", $cursor.pos())),
21 Some(b'=') => {
22 $cursor.next_byte()?.expect("= token vanished");
23 $cursor.next_column();
24 $assign_op
25 }
26 Some(_) => $op,
27 }
28 });
29 ($cursor:ident, $assign_op:expr, $op:expr, {$($case:pat => $block:expr), +}) => ({
30 match $cursor.peek()? {
31 None => Err(Error::syntax("abrupt end - could not preview next value as part of the operator", $cursor.pos())),
32 Some(b'=') => {
33 $cursor.next_byte()?.expect("= token vanished");
34 $cursor.next_column();
35 $assign_op
36 },
37 $($case => {
38 $cursor.next_byte()?.expect("Token vanished");
39 $cursor.next_column();
40 $block
41 })+,
42 _ => $op,
43 }
44 });
45 ($cursor:ident, $op:expr, {$($case:pat => $block:expr),+}) => {
46 match $cursor.peek().ok_or_else(|| Error::syntax("could not preview next value", $cursor.pos()))? {
47 $($case => {
48 $cursor.next_byte()?;
49 $cursor.next_column();
50 $block
51 })+,
52 _ => $op
53 }
54 }
55}
56
57macro_rules! op {
59 ($cursor:ident, $start_pos:expr, $assign_op:expr, $op:expr) => ({
60 Ok(Token::new(
61 vop!($cursor, $assign_op, $op)?.into(),
62 Span::new($start_pos, $cursor.pos()),
63 ))
64 });
65 ($cursor:ident, $start_pos:expr, $assign_op:expr, $op:expr, {$($case:pat => $block:expr),+}) => ({
66 let punc: Punctuator = vop!($cursor, $assign_op, $op, {$($case => $block),+})?;
67 Ok(Token::new(
68 punc.into(),
69 Span::new($start_pos, $cursor.pos()),
70 ))
71 });
72}
73
74#[derive(Debug, Clone, Copy)]
75pub(super) struct Operator {
76 init: u8,
77}
78
79impl Operator {
90 pub(super) fn new(init: u8) -> Self {
92 Self { init }
93 }
94}
95
96impl<R> Tokenizer<R> for Operator {
97 fn lex(&mut self, cursor: &mut Cursor<R>, start_pos: Position) -> Result<Token, Error>
98 where
99 R: Read,
100 {
101 let _timer = BoaProfiler::global().start_event("Operator", "Lexing");
102
103 match self.init {
104 b'*' => op!(cursor, start_pos, Ok(Punctuator::AssignMul), Ok(Punctuator::Mul), {
105 Some(b'*') => vop!(cursor, Ok(Punctuator::AssignPow), Ok(Punctuator::Exp))
106 }),
107 b'+' => op!(cursor, start_pos, Ok(Punctuator::AssignAdd), Ok(Punctuator::Add), {
108 Some(b'+') => Ok(Punctuator::Inc)
109 }),
110 b'-' => op!(cursor, start_pos, Ok(Punctuator::AssignSub), Ok(Punctuator::Sub), {
111 Some(b'-') => {
112 Ok(Punctuator::Dec)
113 }
114 }),
115 b'%' => op!(
116 cursor,
117 start_pos,
118 Ok(Punctuator::AssignMod),
119 Ok(Punctuator::Mod)
120 ),
121 b'|' => op!(cursor, start_pos, Ok(Punctuator::AssignOr), Ok(Punctuator::Or), {
122 Some(b'|') => vop!(cursor, Ok(Punctuator::AssignBoolOr), Ok(Punctuator::BoolOr))
123 }),
124 b'&' => op!(cursor, start_pos, Ok(Punctuator::AssignAnd), Ok(Punctuator::And), {
125 Some(b'&') => vop!(cursor, Ok(Punctuator::AssignBoolAnd), Ok(Punctuator::BoolAnd))
126 }),
127 b'?' => match cursor.peek()? {
128 Some(b'?') => {
129 let _ = cursor.next_byte()?.expect("? vanished");
130 op!(
131 cursor,
132 start_pos,
133 Ok(Punctuator::AssignCoalesce),
134 Ok(Punctuator::Coalesce)
135 )
136 }
137 _ => Ok(Token::new(
138 TokenKind::Punctuator(Punctuator::Question),
139 Span::new(start_pos, cursor.pos()),
140 )),
141 },
142 b'^' => op!(
143 cursor,
144 start_pos,
145 Ok(Punctuator::AssignXor),
146 Ok(Punctuator::Xor)
147 ),
148 b'=' => op!(cursor, start_pos, if cursor.next_is(b'=')? {
149 Ok(Punctuator::StrictEq)
150 } else {
151 Ok(Punctuator::Eq)
152 }, Ok(Punctuator::Assign), {
153 Some(b'>') => {
154 Ok(Punctuator::Arrow)
155 }
156 }),
157 b'<' => {
158 op!(cursor, start_pos, Ok(Punctuator::LessThanOrEq), Ok(Punctuator::LessThan), {
159 Some(b'<') => vop!(cursor, Ok(Punctuator::AssignLeftSh), Ok(Punctuator::LeftSh))
160 })
161 }
162 b'>' => {
163 op!(cursor, start_pos, Ok(Punctuator::GreaterThanOrEq), Ok(Punctuator::GreaterThan), {
164 Some(b'>') => vop!(cursor, Ok(Punctuator::AssignRightSh), Ok(Punctuator::RightSh), {
165 Some(b'>') => vop!(cursor, Ok(Punctuator::AssignURightSh), Ok(Punctuator::URightSh))
166 })
167 })
168 }
169 b'!' => op!(
170 cursor,
171 start_pos,
172 vop!(cursor, Ok(Punctuator::StrictNotEq), Ok(Punctuator::NotEq)),
173 Ok(Punctuator::Not)
174 ),
175 b'~' => Ok(Token::new(
176 Punctuator::Neg.into(),
177 Span::new(start_pos, cursor.pos()),
178 )),
179 op => unimplemented!("operator {}", op),
180 }
181 }
182}