logix_type/token/
mod.rs

1//! The `Token` type and other relevant types returned by `LogixParser::next_token`
2
3use std::{borrow::Cow, fmt};
4
5use crate::error::TokenError;
6
7mod comment;
8mod parse;
9mod string;
10pub use self::{
11    parse::{parse_token, ParseRes},
12    string::StrLit,
13};
14
15struct ByteSet(&'static str);
16
17#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
18pub enum StrTag {
19    /// The string can be used as is, no pre-processing needed
20    Raw,
21    /// The string contain basic backslash escaped data
22    Esc,
23    /// The string is in nicely wrapped multi-line format
24    /// * No escapes
25    /// * Remove common leading whitespace
26    /// * Trim the end of the string
27    /// * Remove all single newlines and replace them by space (paragraph)
28    Txt,
29}
30
31impl StrTag {
32    const VALID: ByteSet = ByteSet("abcdefghijklmnopqrstuvwxyz0123456789-_");
33
34    fn from_prefix(buf: &[u8]) -> Option<(usize, Self)> {
35        if buf.starts_with(b"raw\"") {
36            Some((4, Self::Raw))
37        } else if buf.starts_with(b"esc\"") {
38            Some((4, Self::Esc))
39        } else if buf.starts_with(b"txt\"") {
40            Some((4, Self::Txt))
41        } else {
42            None
43        }
44    }
45}
46
47impl fmt::Display for StrTag {
48    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49        match self {
50            Self::Raw => write!(f, "`#raw`"),
51            Self::Esc => write!(f, "`#esc`"),
52            Self::Txt => write!(f, "`#txt`"),
53        }
54    }
55}
56
57#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
58pub struct StrTagSuffix(Cow<'static, str>);
59
60impl StrTagSuffix {
61    pub fn len(&self) -> usize {
62        self.0.len()
63    }
64}
65
66impl StrTagSuffix {
67    pub fn new(num_hashes: usize) -> Self {
68        static HASHES: &str = "\"################################";
69        Self(if num_hashes < HASHES.len() {
70            Cow::Borrowed(&HASHES[..num_hashes + 1])
71        } else {
72            let mut s = String::with_capacity(num_hashes + 1);
73            s.push('"');
74            s.extend(std::iter::repeat('#').take(num_hashes));
75            Cow::Owned(s)
76        })
77    }
78}
79
80impl AsRef<[u8]> for StrTagSuffix {
81    fn as_ref(&self) -> &[u8] {
82        self.0.as_ref().as_ref()
83    }
84}
85
86impl fmt::Display for StrTagSuffix {
87    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88        write!(f, "`{}`", self.0)
89    }
90}
91
92#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
93pub enum Brace {
94    /// Curly braces `{}`
95    Curly,
96    /// Parenthesis `()`
97    Paren,
98    /// Square brackets `[]`
99    Square,
100    /// AAngle brackets `<>`
101    Angle,
102}
103
104#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
105pub enum Delim {
106    Colon,
107    Comma,
108}
109
110#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
111pub enum Literal<'a> {
112    Str(StrLit<'a>),
113    Num(&'a str),
114}
115
116#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
117pub enum Action {
118    Include,
119}
120
121#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy)]
122pub enum Token<'a> {
123    Ident(&'a str),
124    Action(Action),
125    Literal(Literal<'a>),
126    Brace {
127        start: bool,
128        brace: Brace,
129    },
130    Delim(Delim),
131    Comment(&'a str),
132    /// Indicates a newline, argument is true if it is the last one (aka EOF)
133    Newline(bool),
134}
135
136impl<'a> Token<'a> {
137    pub fn token_type_name(&self) -> &'static str {
138        match self {
139            Self::Ident(_) => "identifier",
140            Self::Action(_) => "action",
141            Self::Literal(Literal::Str(..)) => "string",
142            Self::Literal(Literal::Num(..)) => "number",
143            Self::Brace {
144                start: true,
145                brace: Brace::Paren,
146            } => "`(`",
147            Self::Brace {
148                start: false,
149                brace: Brace::Paren,
150            } => "`)`",
151            Self::Brace {
152                start: true,
153                brace: Brace::Curly,
154            } => "`{`",
155            Self::Brace {
156                start: false,
157                brace: Brace::Curly,
158            } => "`}`",
159            Self::Brace {
160                start: true,
161                brace: Brace::Square,
162            } => "`[`",
163            Self::Brace {
164                start: false,
165                brace: Brace::Square,
166            } => "`]`",
167            Self::Brace {
168                start: true,
169                brace: Brace::Angle,
170            } => "`<`",
171            Self::Brace {
172                start: false,
173                brace: Brace::Angle,
174            } => "`>`",
175            Self::Delim(Delim::Comma) => "`,`",
176            Self::Delim(Delim::Colon) => "`:`",
177            Self::Newline(false) => "newline",
178            Self::Newline(true) => "end of file",
179            Self::Comment(..) => "comment",
180        }
181    }
182
183    pub fn write_token_display_name(&self, f: &mut impl fmt::Write) -> fmt::Result {
184        match self {
185            Self::Ident(value) => write!(f, "`{value}`"),
186            Self::Action(Action::Include) => write!(f, "`@include`"),
187            Self::Literal(Literal::Num(num)) => write!(f, "`{num}`"),
188            Self::Brace { .. }
189            | Self::Delim(..)
190            | Self::Newline(..)
191            | Self::Literal(Literal::Str(..))
192            | Self::Comment(..) => {
193                write!(f, "{}", self.token_type_name())
194            }
195        }
196    }
197}
198
199#[cfg(test)]
200mod tests {
201    use super::*;
202
203    #[test]
204    fn print_comment_token_type() {
205        // NOTE(2023.10): I haven't found a way to print it in external tests so doing it directly
206        let token = Token::Comment("hello");
207        assert_eq!(token.token_type_name().to_string(), "comment");
208        {
209            let mut s = String::new();
210            token.write_token_display_name(&mut s).unwrap();
211            assert_eq!(s, "comment");
212        }
213    }
214
215    #[test]
216    fn print_number_token_type() {
217        // NOTE(2023.10): I haven't found a way to print it in external tests so doing it directly
218        let token = Token::Literal(Literal::Num("10"));
219        assert_eq!(token.token_type_name().to_string(), "number");
220        {
221            let mut s = String::new();
222            token.write_token_display_name(&mut s).unwrap();
223            assert_eq!(s, "`10`");
224        }
225    }
226
227    #[test]
228    fn print_str_token_type() {
229        // NOTE(2023.10): I haven't found a way to print it in external tests so doing it directly
230        let token = Token::Literal(Literal::Str(StrLit::new(StrTag::Raw, "aa")));
231        assert_eq!(token.token_type_name().to_string(), "string");
232        {
233            let mut s = String::new();
234            token.write_token_display_name(&mut s).unwrap();
235            assert_eq!(s, "string");
236        }
237    }
238
239    #[test]
240    fn print_brace_token_type() {
241        // NOTE(2023.10): I haven't found a way to print it in external tests so doing it directly
242        let token = Token::Brace {
243            start: true,
244            brace: Brace::Curly,
245        };
246        assert_eq!(token.token_type_name().to_string(), "`{`");
247        {
248            let mut s = String::new();
249            token.write_token_display_name(&mut s).unwrap();
250            assert_eq!(s, "`{`");
251        }
252    }
253}