1use std::vec::IntoIter;
2
3use chumsky::input::Stream;
4use logos::Logos as _;
5
6use super::lexer;
7
8#[macro_export]
24macro_rules! impl_block_properties_parser {
25 (@build_or_chain $first_parser_var:ident) => {
26 $first_parser_var
27 };
28 (@build_or_chain $first_parser_var:ident, $($rest_parser_vars:ident),+) => {
29 $first_parser_var.or(impl_block_properties_parser!(@build_or_chain $($rest_parser_vars),+))
30 };
31
32 (
33 $any_property_let_name:ident: $PropEnumType:ty = {
34 $(
35 $var_name:ident = $parser_call_expr:expr => $value_mapper_fn:expr
36 ),+ $(,)? }
38 ) => {
39 $(
40 let $var_name = $parser_call_expr.map($value_mapper_fn);
41 )+
42 let $any_property_let_name =
43 impl_block_properties_parser!(@build_or_chain $($var_name),+).boxed();
44 };
45}
46
47pub fn lex(input: &str) -> Stream<IntoIter<lexer::Token<'_>>> {
49 Stream::from_iter(
50 lexer::Token::lexer(input)
51 .map(|tok| tok.expect("expected a valid token."))
52 .collect::<Vec<lexer::Token<'_>>>(),
53 )
54}
55
56pub fn tokenize(input: &str) -> Vec<lexer::Token<'_>> {
58 lexer::Token::lexer(input)
59 .enumerate()
60 .map(|(idx, tok)| {
61 tok.unwrap_or_else(|_| {
62 let lexer = lexer::Token::lexer(input);
63 let span = lexer.span();
64 let context = &input
65 [span.start.saturating_sub(20)..span.end.saturating_add(20).min(input.len())];
66 panic!(
67 "Failed to tokenize at position {} (token #{})\nContext: {:?}",
68 span.start, idx, context
69 );
70 })
71 })
72 .collect()
73}
74
75pub fn stream(tokens: Vec<lexer::Token<'_>>) -> Stream<IntoIter<lexer::Token<'_>>> {
77 Stream::from_iter(tokens)
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83
84 #[test]
85 fn test_tokenize_simple() {
86 let input = r#"world
87 {
88 "id" "1"
89 }"#;
90 let tokens = tokenize(input);
91 println!("Tokens: {:#?}", tokens);
92 }
93
94 #[test]
95 fn test_tokenize_with_special_chars() {
96 let input = r#""detail/detailsprites""#;
97 let tokens = tokenize(input);
98 println!("Tokens: {:#?}", tokens);
99 }
100
101 #[test]
102 fn test_tokenize_with_brackets() {
103 let input = r#""[1 0 0 0]""#;
104 let tokens = tokenize(input);
105 println!("Tokens: {:#?}", tokens);
106 }
107
108 #[test]
109 fn test_tokenize_negative_number() {
110 let input = r#""-1""#;
111 let tokens = tokenize(input);
112 println!("Tokens: {:#?}", tokens);
113 }
114}