1mod context_stack;
2
3use context_stack::ContextStack;
4use huff_neo_utils::file::full_file_source::FullFileSource;
5use huff_neo_utils::lexer_context::Context;
6use huff_neo_utils::prelude::*;
7use lazy_static::lazy_static;
8use regex::Regex;
9use std::collections::HashMap;
10use std::{iter::Peekable, str::Chars};
11use tracing::{debug, error};
12
13lazy_static! {
14 static ref TOKEN: HashMap<String, TokenKind> = HashMap::from_iter(vec![
15 (TokenKind::Macro.to_string(), TokenKind::Macro),
16 (TokenKind::Fn.to_string(), TokenKind::Fn),
17 (TokenKind::Test.to_string(), TokenKind::Test),
18 (TokenKind::Function.to_string(), TokenKind::Function),
19 (TokenKind::Constant.to_string(), TokenKind::Constant),
20 (TokenKind::Error.to_string(), TokenKind::Error),
21 (TokenKind::Takes.to_string(), TokenKind::Takes),
22 (TokenKind::Returns.to_string(), TokenKind::Returns),
23 (TokenKind::Event.to_string(), TokenKind::Event),
24 (TokenKind::NonPayable.to_string(), TokenKind::NonPayable),
25 (TokenKind::Payable.to_string(), TokenKind::Payable),
26 (TokenKind::Indexed.to_string(), TokenKind::Indexed),
27 (TokenKind::View.to_string(), TokenKind::View),
28 (TokenKind::Pure.to_string(), TokenKind::Pure),
29 (TokenKind::JumpTablePacked.to_string(), TokenKind::JumpTablePacked),
31 (TokenKind::JumpTable.to_string(), TokenKind::JumpTable),
33 (TokenKind::CodeTable.to_string(), TokenKind::CodeTable),
34 ]);
35}
36
37const MAX_HEX_LITERAL_LENGTH: usize = 66;
39
40pub struct Lexer<'a> {
44 pub chars: Peekable<Chars<'a>>,
47 position: usize,
49 byte_offset: usize,
51 pub lookback: Option<Token>,
54 pub eof: bool,
56 context_stack: ContextStack,
58 decorator_depth: usize,
60 pub source: FullFileSource<'a>,
62}
63
64pub type TokenResult = Result<Token, LexicalError>;
65
66impl<'a> Lexer<'a> {
67 pub fn new(source: FullFileSource<'a>) -> Self {
68 Lexer {
69 chars: source.source.chars().peekable(),
70 position: 0,
71 byte_offset: 0,
72 lookback: None,
73 eof: false,
74 context_stack: ContextStack::new(),
75 decorator_depth: 0,
76 source,
77 }
78 }
79
80 pub fn consume(&mut self) -> Option<char> {
84 let c = self.chars.next()?;
85 self.position = self.byte_offset;
86 self.byte_offset += c.len_utf8();
87 Some(c)
88 }
89
90 pub fn peek(&mut self) -> Option<char> {
92 self.chars.peek().copied()
93 }
94
95 fn next_token(&mut self) -> TokenResult {
96 if let Some(ch) = self.consume() {
97 let token = match ch {
98 '/' => {
99 let mut comment_string = String::new();
100 let start = self.position;
101 comment_string.push(ch);
102 if let Some(ch2) = self.peek() {
103 match ch2 {
104 '/' => {
105 comment_string.push(ch2);
107 let (comment_string, start, end) = self.eat_while(Some(ch), |c| c != '\n');
108 Ok(TokenKind::Comment(comment_string).into_token_with_span(self.source.relative_span_by_pos(start, end)))
109 }
110 '*' => {
111 let c = self.consume();
113 comment_string.push(c.unwrap());
114 let mut depth = 1usize;
115 while let Some(c) = self.consume() {
116 match c {
117 '/' if self.peek() == Some('*') => {
118 comment_string.push(c);
119 let c2 = self.consume();
120 comment_string.push(c2.unwrap());
121 depth += 1;
122 }
123 '*' if self.peek() == Some('/') => {
124 comment_string.push(c);
125 let c2 = self.consume();
126 comment_string.push(c2.unwrap());
127 depth -= 1;
128 if depth == 0 {
129 break;
135 }
136 }
137 _ => {
138 comment_string.push(c);
139 }
140 }
141 }
142
143 Ok(TokenKind::Comment(comment_string)
144 .into_token_with_span(self.source.relative_span_by_pos(start, self.byte_offset)))
145 }
146 _ => self.single_char_token(TokenKind::Div),
147 }
148 } else {
149 self.single_char_token(TokenKind::Div)
150 }
151 }
152
153 '#' => {
155 let (word, start, end) = self.eat_while(Some(ch), |ch| ch.is_ascii_alphabetic());
156
157 let mut found_kind: Option<TokenKind> = None;
158
159 let keys = [TokenKind::Define, TokenKind::Include];
160 for kind in keys.into_iter() {
161 let key = kind.to_string();
162 if key == word {
163 found_kind = Some(kind);
164 break;
165 }
166 }
167
168 if let Some(kind) = found_kind {
169 Ok(kind.into_token_with_span(self.source.relative_span_by_pos(start, end)))
170 } else if matches!(self.context_stack.top(), Context::Global | Context::Constant | Context::Abi)
171 && self.peek().unwrap() == '['
172 {
173 if self.context_stack.top() != &Context::Global {
175 debug!(target: "lexer", "POP CONTEXT {:?}", self.context_stack.top());
176 self.context_stack.pop(1).map_err(|_| {
177 LexicalError::new(LexicalErrorKind::StackUnderflow, self.source.relative_span_by_pos(start, end))
178 })?;
179 }
180 debug!(target: "lexer", "PUSH CONTEXT Decorator");
181 self.context_stack.push(Context::Decorator);
182 self.decorator_depth = 0;
183 Ok(TokenKind::Pound.into_token_with_span(self.source.relative_span_by_pos(start, start + 1)))
185 } else {
186 error!(target: "lexer", "INVALID '#' CHARACTER USAGE in context {:?}", self.context_stack.top());
188 return Err(LexicalError::new(
189 LexicalErrorKind::InvalidCharacter('#'),
190 self.source.relative_span_by_pos(start, end),
191 ));
192 }
193 }
194 ch if ch.is_alphabetic() || ch.eq(&'_') => {
196 let (word, start, mut end) = self.eat_while(Some(ch), |c| c.is_alphanumeric() || c == '_');
197
198 let mut found_kind: Option<TokenKind> = None;
199 if !matches!(self.context_stack.top(), &Context::MacroBody | &Context::Decorator)
201 && let Some(kind) = TOKEN.get(&word)
202 {
203 debug!(target: "lexer", "FOUND KEYWORD OUTSIDE OF MacroBody '{:?}'", kind);
204 found_kind = Some(kind.clone());
205 }
206
207 if !self.check_keyword_rules(&found_kind) {
211 debug!(target: "lexer", "RESET KEYWORD TO NONE '{:?}'", found_kind);
212 found_kind = None;
213 }
214
215 if let Some(kind) = &found_kind {
217 let mut new_context = None;
218 match kind {
219 TokenKind::Macro | TokenKind::Fn | TokenKind::Test => {
220 debug!(target: "lexer", "PUSH CONTEXT MacroDefinition");
221 new_context = Some(Context::MacroDefinition);
222 }
223 TokenKind::Function | TokenKind::Event | TokenKind::Error => {
224 debug!(target: "lexer", "PUSH CONTEXT Abi");
225 new_context = Some(Context::Abi);
226 }
227 TokenKind::Constant => {
228 debug!(target: "lexer", "PUSH CONTEXT Constant");
229 new_context = Some(Context::Constant);
230 }
231 TokenKind::CodeTable => {
232 debug!(target: "lexer", "PUSH CONTEXT CodeTableBody");
233 new_context = Some(Context::CodeTableBody);
234 }
235 _ => (),
236 }
237
238 if new_context.is_some() && self.context_stack.top() != &Context::Global {
240 debug!(target: "lexer", "POP CONTEXT {:?}", self.context_stack.top());
241 self.context_stack.pop(1).map_err(|_| {
242 LexicalError::new(LexicalErrorKind::StackUnderflow, self.source.relative_span_by_pos(start, end))
243 })?;
244 }
245 if new_context.is_some() && self.context_stack.top() != &Context::Global {
247 return Err(LexicalError::new(
248 LexicalErrorKind::UnexpectedContext(self.context_stack.top().clone()),
249 self.source.relative_span_by_pos(start, end),
250 ));
251 }
252 if let Some(new_context) = new_context {
254 self.context_stack.push(new_context);
255 }
256 }
257
258 if word == "FREE_STORAGE_POINTER" {
260 if let Some('(') = self.peek() {
264 self.consume();
265 }
266 if let Some(')') = self.peek() {
267 self.consume();
268 }
269 end += 2;
270 debug!(target: "lexer", "FOUND FREE_STORAGE_POINTER");
271 found_kind = Some(TokenKind::FreeStoragePointer);
272 }
273
274 if word == "__NOOP"
276 && matches!(
277 self.context_stack.top(),
278 &Context::MacroBody | &Context::ForLoopBody | &Context::Constant | &Context::MacroArgs
279 )
280 {
281 debug!(target: "lexer", "FOUND __NOOP");
282 found_kind = Some(TokenKind::Noop);
283 }
284
285 if let Some(':') = self.peek() {
286 debug!(target: "lexer", "FOUND LABEL '{:?}'", word);
287 found_kind = Some(TokenKind::Label(word.clone()));
288 }
289
290 let is_arg_reference =
293 self.checked_lookback(TokenKind::LeftAngle) && self.chars.clone().find(|c| !c.is_whitespace()) == Some('>');
294
295 if !is_arg_reference
297 && matches!(self.context_stack.top(), &Context::MacroBody | &Context::ForLoopBody | &Context::Constant | &Context::MacroArgs)
298 && !self.checked_lookback(TokenKind::Constant) && matches!(word.as_str(), "true" | "false")
300 {
301 debug!(target: "lexer", "FOUND BOOL '{:?}'", word);
302 found_kind = Some(TokenKind::HexLiteral(if word.as_str() == "true" { "01".to_string() } else { "00".to_string() }));
303 self.eat_while(None, |c| c.is_alphanumeric());
304 }
305
306 if matches!(self.context_stack.top(), &Context::MacroBody | &Context::ForLoopBody)
308 && found_kind.is_none()
309 && !is_arg_reference
310 {
311 found_kind = match word.as_str() {
312 "for" => Some(TokenKind::For),
313 "in" => Some(TokenKind::In),
314 "step" => Some(TokenKind::Step),
315 "if" => Some(TokenKind::If),
316 "else" => Some(TokenKind::Else),
317 _ => OPCODES_MAP.get(&word).map(|o| TokenKind::Opcode(o.to_owned())),
318 };
319 if let Some(kind) = &found_kind {
320 debug!(target: "lexer", "FOUND {:?}", kind);
321 }
322 }
323
324 if self.context_stack.top() == &Context::AbiArgs {
325 self.eat_abi_args(ch, &word, start, &mut end, &mut found_kind);
326 }
327
328 let kind = if let Some(kind) = found_kind {
329 kind
330 } else if matches!(
331 self.context_stack.top(),
332 &Context::MacroBody
333 | &Context::BuiltinFunction
334 | &Context::CodeTableBody
335 | &Context::Constant
336 | &Context::MacroArgs
337 | &Context::ForLoopBody
338 | &Context::Decorator
339 ) && BuiltinFunctionKind::try_from(&word).is_ok()
340 {
341 TokenKind::BuiltinFunction(word)
342 } else {
343 TokenKind::Ident(word)
344 };
345
346 Ok(kind.into_token_with_span(self.source.relative_span_by_pos(start, end)))
347 }
348 ch if ch == '0' && self.peek().unwrap() == 'x' => self.eat_hex_digit(ch),
350 '=' => {
351 if self.peek() == Some('=') {
353 let start = self.position;
354 self.consume(); Ok(TokenKind::EqualEqual.into_token_with_span(self.source.relative_span_by_pos(start, self.byte_offset)))
356 } else {
357 self.single_char_token(TokenKind::Assign)
358 }
359 }
360 '!' => {
361 if self.peek() == Some('=') {
363 let start = self.position;
364 self.consume(); Ok(TokenKind::NotEqual.into_token_with_span(self.source.relative_span_by_pos(start, self.byte_offset)))
366 } else {
367 self.single_char_token(TokenKind::Not)
369 }
370 }
371 '(' => {
372 match self.context_stack.top() {
373 Context::Abi | Context::AbiArgs => {
375 debug!(target: "lexer", "PUSH CONTEXT AbiArgs");
376 self.context_stack.push(Context::AbiArgs)
377 }
378 Context::Decorator => {
379 if let Some(TokenKind::BuiltinFunction(_)) = self.lookback.as_ref().map(|t| &t.kind) {
380 debug!(target: "lexer", "PUSH CONTEXT BuiltinFunction");
381 self.context_stack.push(Context::BuiltinFunction)
382 }
383 }
384 Context::MacroBody | Context::ForLoopBody => match self.lookback.as_ref().unwrap().kind {
385 TokenKind::BuiltinFunction(_) => {
386 debug!(target: "lexer", "PUSH CONTEXT BuiltinFunction");
387 self.context_stack.push(Context::BuiltinFunction)
388 }
389 TokenKind::For => {
390 debug!(target: "lexer", "KEEP CONTEXT for 'for' loop");
393 }
394 _ => {
395 debug!(target: "lexer", "PUSH CONTEXT MacroArgs");
396 self.context_stack.push(Context::MacroArgs)
397 }
398 },
399 _ => {}
400 }
401 self.single_char_token(TokenKind::OpenParen)
402 }
403 ')' => {
404 if matches!(self.context_stack.top(), &Context::AbiArgs | &Context::BuiltinFunction | &Context::MacroArgs) {
405 debug!(target: "lexer", "POP CONTEXT AbiArgs OR BuiltinFunction OR MacroArgs");
406 self.context_stack.pop(1).map_err(|_| {
407 LexicalError::new(
408 LexicalErrorKind::StackUnderflow,
409 self.source.relative_span_by_pos(self.position, self.byte_offset),
410 )
411 })?
412 }
413 self.single_char_token(TokenKind::CloseParen)
414 }
415 '[' => {
416 if self.context_stack.top() == &Context::Decorator {
417 self.decorator_depth += 1;
418 }
419 self.single_char_token(TokenKind::OpenBracket)
420 }
421 ']' => {
422 if self.context_stack.top() == &Context::Decorator {
423 self.decorator_depth = self.decorator_depth.saturating_sub(1);
424 if self.decorator_depth == 0 {
425 debug!(target: "lexer", "POP CONTEXT Decorator");
426 self.context_stack.pop(1).map_err(|_| {
427 LexicalError::new(
428 LexicalErrorKind::StackUnderflow,
429 self.source.relative_span_by_pos(self.position, self.byte_offset),
430 )
431 })?;
432 }
433 }
434 self.single_char_token(TokenKind::CloseBracket)
435 }
436 '{' => {
437 if matches!(self.context_stack.top(), &Context::MacroBody | &Context::ForLoopBody) {
439 debug!(target: "lexer", "PUSH CONTEXT ForLoopBody");
440 self.context_stack.push(Context::ForLoopBody);
441 }
442
443 if self.context_stack.top() == &Context::MacroDefinition {
444 debug!(target: "lexer", "REPLACE CONTEXT MacroDefinition to MacroBody");
446 self.context_stack.replace(Context::MacroBody);
447 }
448 self.single_char_token(TokenKind::OpenBrace)
449 }
450 '}' => {
451 if matches!(self.context_stack.top(), &Context::ForLoopBody | &Context::MacroBody | &Context::CodeTableBody) {
452 debug!(target: "lexer", "POP CONTEXT {:?}", self.context_stack.top());
453 self.context_stack.pop(1).map_err(|_| {
454 LexicalError::new(
455 LexicalErrorKind::StackUnderflow,
456 self.source.relative_span_by_pos(self.position, self.byte_offset),
457 )
458 })?;
459 }
460 self.single_char_token(TokenKind::CloseBrace)
461 }
462 '+' => self.single_char_token(TokenKind::Add),
463 '-' => self.single_char_token(TokenKind::Sub),
464 '*' => self.single_char_token(TokenKind::Mul),
465 '%' => self.single_char_token(TokenKind::Mod),
466 '<' => {
467 if self.peek() == Some('=') {
469 let start = self.position;
470 self.consume(); Ok(TokenKind::LessEqual.into_token_with_span(self.source.relative_span_by_pos(start, self.byte_offset)))
472 } else {
473 self.single_char_token(TokenKind::LeftAngle)
474 }
475 }
476 '>' => {
477 if self.peek() == Some('=') {
479 let start = self.position;
480 self.consume(); Ok(TokenKind::GreaterEqual.into_token_with_span(self.source.relative_span_by_pos(start, self.byte_offset)))
482 } else {
483 self.single_char_token(TokenKind::RightAngle)
484 }
485 }
486 ':' => self.single_char_token(TokenKind::Colon),
488 '.' => {
489 if self.peek() == Some('.') {
491 let start = self.position;
492 self.consume(); Ok(TokenKind::DoubleDot.into_token_with_span(self.source.relative_span_by_pos(start, self.byte_offset)))
494 } else {
495 error!(target: "lexer", "UNSUPPORTED TOKEN '.'");
497 return Err(LexicalError::new(
498 LexicalErrorKind::InvalidCharacter('.'),
499 self.source.relative_span_by_pos(self.position, self.byte_offset),
500 ));
501 }
502 }
503 ',' => self.single_char_token(TokenKind::Comma),
505 '0'..='9' => self.eat_digit(ch),
506 ch if ch.is_ascii_whitespace() => {
508 let (_, start, end) = self.eat_while(Some(ch), |c| c.is_whitespace());
509 Ok(TokenKind::Whitespace.into_token_with_span(self.source.relative_span_by_pos(start, end)))
510 }
511 '"' | '\'' => Ok(self.eat_string_literal()),
513 ch => {
514 error!(target: "lexer", "UNSUPPORTED TOKEN '{}'", ch);
515 return Err(LexicalError::new(
516 LexicalErrorKind::InvalidCharacter(ch),
517 self.source.relative_span_by_pos(self.position, self.byte_offset),
518 ));
519 }
520 }?;
521
522 if token.kind != TokenKind::Whitespace {
523 self.lookback = Some(token.clone());
524 }
525
526 Ok(token)
527 } else {
528 self.eof = true;
529 let eof_pos = self.source.source.len();
530 Ok(Token { kind: TokenKind::Eof, span: self.source.relative_span_by_pos(eof_pos, eof_pos) })
531 }
532 }
533
534 fn eat_abi_args(&mut self, ch: char, word: &String, start: usize, end: &mut usize, found_kind: &mut Option<TokenKind>) {
535 let curr_char = self.peek().unwrap();
536 if !['(', ')'].contains(&curr_char) {
537 let (partial_raw_type, _, abi_args_end) = self.eat_while(Some(ch), |c| c.is_alphanumeric() || c == '[' || c == ']');
538 let raw_type = word.clone() + &partial_raw_type[1..];
539
540 if raw_type == TokenKind::Calldata.to_string() {
541 debug!(target: "lexer", "FOUND CALLED DATA '{:?}'", raw_type);
542 *found_kind = Some(TokenKind::Calldata);
543 } else if raw_type == TokenKind::Memory.to_string() {
544 debug!(target: "lexer", "FOUND MEMORY '{:?}'", raw_type);
545 *found_kind = Some(TokenKind::Memory);
546 } else if raw_type == TokenKind::Storage.to_string() {
547 debug!(target: "lexer", "FOUND STORAGE '{:?}'", raw_type);
548 *found_kind = Some(TokenKind::Storage);
549 } else if EVM_TYPE_ARRAY_REGEX.is_match(&raw_type) {
550 let words: Vec<String> = Regex::new(r"\[").unwrap().split(&raw_type).map(|x| x.replace(']', "")).collect();
553 let mut size_vec: Vec<usize> = Vec::new();
554 let sizes = words.get(1..words.len()).unwrap();
556 for size in sizes.iter() {
557 match size.is_empty() {
558 true => size_vec.push(0),
559 false => {
560 let arr_size: usize = size
561 .parse::<usize>()
562 .map_err(|_| {
563 let err = LexicalError {
564 kind: LexicalErrorKind::InvalidArraySize(words[1].clone()),
565 span: self.source.relative_span_by_pos(start, *end),
566 };
567 error!(target: "lexer", "{}", format!("{err:?}"));
568 err
569 })
570 .unwrap();
571 size_vec.push(arr_size);
572 }
573 }
574 }
575 let primitive = PrimitiveEVMType::try_from(&words[0]);
576 if let Ok(primitive) = primitive {
577 debug!(target: "lexer", "FOUND ARRAY '{:?}'", raw_type);
578 *found_kind = Some(TokenKind::ArrayType(primitive, size_vec));
579 } else {
580 let err = LexicalError {
581 kind: LexicalErrorKind::InvalidPrimitiveType(words[0].clone()),
582 span: self.source.relative_span_by_pos(start, *end),
583 };
584 error!(target: "lexer", "{}", format!("{err:?}"));
585 }
586 } else {
587 let primitive = PrimitiveEVMType::try_from(word);
590 if let Ok(primitive) = primitive {
591 debug!(target: "lexer", "FOUND PRIMITIVE '{:?}'", raw_type);
592 *found_kind = Some(TokenKind::PrimitiveType(primitive));
593 }
594 }
595 *end = abi_args_end;
596 } else {
597 let primitive = PrimitiveEVMType::try_from(word);
600 if let Ok(primitive) = primitive {
601 debug!(target: "lexer", "FOUND PRIMITIVE '{:?}'", word);
602 *found_kind = Some(TokenKind::PrimitiveType(primitive));
603 }
604 }
605 }
606
607 fn single_char_token(&self, token_kind: TokenKind) -> TokenResult {
608 Ok(token_kind.into_token_with_span(self.source.relative_span_by_pos(self.position, self.byte_offset)))
609 }
610
611 fn eat_while<F: Fn(char) -> bool>(&mut self, initial_char: Option<char>, predicate: F) -> (String, usize, usize) {
613 let start = self.position;
614
615 let mut word = String::new();
619 if let Some(init_char) = initial_char {
620 word.push(init_char)
621 }
622
623 while let Some(peek_char) = self.peek() {
625 if !predicate(peek_char) {
629 return (word, start, self.byte_offset);
630 }
631 word.push(peek_char);
632
633 self.consume();
636 }
637
638 (word, start, self.byte_offset)
639 }
640
641 fn eat_digit(&mut self, initial_char: char) -> TokenResult {
642 let (integer_str, start, end) = self.eat_while(Some(initial_char), |ch| ch.is_ascii_digit());
643
644 let integer = integer_str.parse().unwrap();
645 let integer_token = TokenKind::Integer(integer);
646
647 Ok(Token { kind: integer_token, span: self.source.relative_span_by_pos(start, end) })
648 }
649
650 fn eat_hex_digit(&mut self, initial_char: char) -> TokenResult {
651 let (integer_str, start, end) = self.eat_while(Some(initial_char), |ch| ch.is_ascii_hexdigit() | (ch == 'x'));
652 if integer_str.matches('x').count() != 1 {
653 return Err(LexicalError::new(
654 LexicalErrorKind::InvalidHexLiteral(integer_str.clone()),
655 self.source.relative_span_by_pos(start, end),
656 ));
657 }
658
659 if self.context_stack.top() == &Context::Constant && integer_str.len() > MAX_HEX_LITERAL_LENGTH {
662 return Err(LexicalError::new(
663 LexicalErrorKind::HexLiteralTooLong(integer_str.clone()),
664 self.source.relative_span_by_pos(start, end),
665 ));
666 }
667
668 let hex_string = integer_str[2..].to_lowercase();
671
672 if hex_string.is_empty() {
674 return Err(LexicalError::new(
675 LexicalErrorKind::InvalidHexLiteral(integer_str.clone()),
676 self.source.relative_span_by_pos(start, end),
677 ));
678 }
679
680 let kind = TokenKind::HexLiteral(hex_string);
681
682 Ok(Token { kind, span: self.source.relative_span_by_pos(start, end) })
683 }
684
685 fn eat_whitespace(&mut self) -> (String, usize, usize) {
687 self.eat_while(None, |ch| ch.is_whitespace())
688 }
689
690 fn eat_string_literal(&mut self) -> Token {
691 let (str_literal, start_span, end_span) = self.eat_while(None, |ch| ch != '"' && ch != '\'');
692 let str_literal_token = TokenKind::Str(str_literal);
693 self.consume(); str_literal_token.into_token_with_span(self.source.relative_span_by_pos(start_span, end_span + 1))
695 }
696
697 pub fn checked_lookback(&self, kind: TokenKind) -> bool {
699 self.lookback.as_ref().and_then(|t| if t.kind == kind { Some(true) } else { None }).is_some()
700 }
701
702 pub fn check_keyword_rules(&mut self, found_kind: &Option<TokenKind>) -> bool {
714 match found_kind {
715 Some(TokenKind::Macro)
716 | Some(TokenKind::Fn)
717 | Some(TokenKind::Test)
718 | Some(TokenKind::Function)
719 | Some(TokenKind::Constant)
720 | Some(TokenKind::Error)
721 | Some(TokenKind::Event)
722 | Some(TokenKind::JumpTable)
723 | Some(TokenKind::JumpTablePacked)
724 | Some(TokenKind::CodeTable) => self.checked_lookback(TokenKind::Define),
725 Some(TokenKind::NonPayable) | Some(TokenKind::Payable) | Some(TokenKind::View) | Some(TokenKind::Pure) => {
726 let keys = [TokenKind::NonPayable, TokenKind::Payable, TokenKind::View, TokenKind::Pure, TokenKind::CloseParen];
727 for key in keys {
728 if self.checked_lookback(key) {
729 return true;
730 }
731 }
732 false
733 }
734 Some(TokenKind::Takes) => self.checked_lookback(TokenKind::Assign),
735 Some(TokenKind::Returns) => {
736 self.eat_whitespace();
737 self.peek().unwrap_or(')') == '(' && !self.checked_lookback(TokenKind::Function)
739 }
740 _ => true,
741 }
742 }
743
744 pub fn lex_imports(source: &str) -> Vec<String> {
747 let mut imports = vec![];
748 let mut peekable_source = source.chars().peekable();
749 let mut include_chars_iterator = "#include".chars().peekable();
750 while peekable_source.peek().is_some() {
751 while let Some(nc) = peekable_source.next() {
752 if nc.eq(&'/')
753 && let Some(nnc) = peekable_source.peek()
754 {
755 if nnc.eq(&'/') {
756 while let Some(lc) = &peekable_source.next() {
758 if lc.eq(&'\n') {
759 break;
760 }
761 }
762 } else if nnc.eq(&'*') {
763 while let Some(lc) = peekable_source.next() {
765 if lc.eq(&'*')
766 && let Some(llc) = peekable_source.peek()
767 && *llc == '/'
768 {
769 break;
770 }
771 }
772 }
773 }
774 if include_chars_iterator.peek().is_none() {
775 include_chars_iterator = "#include".chars().peekable();
777
778 while peekable_source.peek().is_some() {
780 if !peekable_source.peek().unwrap().is_whitespace() {
781 break;
782 } else {
783 peekable_source.next();
784 }
785 }
786
787 #[allow(clippy::collapsible_match)]
789 if let Some(char) = peekable_source.peek() {
790 match char {
791 '"' | '\'' => {
792 peekable_source.next();
793 let mut import = String::new();
794 while peekable_source.peek().is_some() {
795 if let Some(c) = peekable_source.next() {
796 if matches!(c, '"' | '\'') {
797 imports.push(import);
798 break;
799 } else {
800 import.push(c);
801 }
802 }
803 }
804 }
805 _ => { }
806 }
807 }
808 } else if nc.ne(&include_chars_iterator.next().unwrap()) {
809 include_chars_iterator = "#include".chars().peekable();
810 break;
811 }
812 }
813 }
814 imports
815 }
816}
817
818impl Iterator for Lexer<'_> {
819 type Item = TokenResult;
820
821 fn next(&mut self) -> Option<Self::Item> {
822 if self.eof { None } else { Some(self.next_token()) }
823 }
824}