rusche/
token.rs

1use std::fmt::{Display, Formatter, Result as FmtResult};
2
3use crate::span::{Loc, Span};
4
5/// The enum that represents a lexical unit of the source code in Rusche.
6#[derive(Clone, Debug)]
7pub enum Token {
8    /// Open parenthesis `(`.
9    OpenParen(Loc),
10
11    /// Close parenthesis `)`.
12    CloseParen(Loc),
13
14    /// Quote `'`.
15    Quote(Loc),
16
17    /// Quasiquote `` ` ``.
18    Quasiquote(Loc),
19
20    /// Unquote `,`.
21    Unquote(Loc),
22
23    /// Unquote-splicing `,@`.
24    UnquoteSplicing(Loc),
25
26    /// A number literal.
27    Num(f64, Span),
28
29    /// A string literal.
30    Str(String, Span),
31
32    /// A symbol.
33    Sym(String, Span),
34}
35
36impl Token {
37    pub fn span(&self) -> Span {
38        match self {
39            Token::OpenParen(loc)
40            | Token::CloseParen(loc)
41            | Token::Quote(loc)
42            | Token::Quasiquote(loc)
43            | Token::Unquote(loc) => Span::new(*loc, loc.with_column_offset(1)),
44            Token::UnquoteSplicing(loc) => Span::new(*loc, loc.with_column_offset(2)),
45            Token::Num(_, span) | Token::Str(_, span) | Token::Sym(_, span) => *span,
46        }
47    }
48}
49
50impl PartialEq for Token {
51    fn eq(&self, other: &Self) -> bool {
52        match (self, other) {
53            (Token::OpenParen(_), Token::OpenParen(_)) => true,
54            (Token::CloseParen(_), Token::CloseParen(_)) => true,
55            (Token::Quote(_), Token::Quote(_)) => true,
56            (Token::Quasiquote(_), Token::Quasiquote(_)) => true,
57            (Token::Unquote(_), Token::Unquote(_)) => true,
58            (Token::UnquoteSplicing(_), Token::UnquoteSplicing(_)) => true,
59            (Token::Num(a, _), Token::Num(b, _)) => a == b,
60            (Token::Str(a, _), Token::Str(b, _)) => a == b,
61            (Token::Sym(a, _), Token::Sym(b, _)) => a == b,
62            _ => false,
63        }
64    }
65}
66
67impl Display for Token {
68    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
69        match self {
70            Token::OpenParen(_) => write!(f, "("),
71            Token::CloseParen(_) => write!(f, ")"),
72            Token::Quote(_) => write!(f, "'"),
73            Token::Quasiquote(_) => write!(f, "`"),
74            Token::Unquote(_) => write!(f, ","),
75            Token::UnquoteSplicing(_) => write!(f, ",@"),
76            Token::Num(value, _) => write!(f, "{}", value),
77            Token::Str(text, _) => write!(f, "\"{}\"", text),
78            Token::Sym(name, _) => write!(f, "{}", name),
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86
87    #[test]
88    fn test_span_fixed_len() {
89        macro_rules! assert_token_span_length_eq {
90            ($length:literal, $token_case:ident) => {
91                let span = Token::$token_case(Loc::new(1, 1)).span();
92                assert_eq!(span.begin.line, span.end.line);
93                assert_eq!($length, span.end.column - span.begin.column);
94            };
95        }
96        assert_token_span_length_eq!(1, OpenParen);
97        assert_token_span_length_eq!(1, CloseParen);
98        assert_token_span_length_eq!(1, Quote);
99        assert_token_span_length_eq!(1, Quasiquote);
100        assert_token_span_length_eq!(1, Unquote);
101        assert_token_span_length_eq!(2, UnquoteSplicing);
102    }
103
104    #[test]
105    fn test_display() {
106        macro_rules! assert_token_format_eq {
107            ($token_case:ident, $formatted:literal) => {
108                assert_eq!(
109                    format!("{}", Token::$token_case(Loc::new(1, 1))),
110                    $formatted
111                );
112            };
113            ($token_case:ident($value:expr), $formatted:literal) => {
114                assert_eq!(
115                    format!(
116                        "{}",
117                        Token::$token_case($value, Span::new(Loc::new(1, 1), Loc::new(1, 2)))
118                    ),
119                    $formatted
120                );
121            };
122        }
123        assert_token_format_eq!(CloseParen, ")");
124        assert_token_format_eq!(Quote, "'");
125        assert_token_format_eq!(Quasiquote, "`");
126        assert_token_format_eq!(Unquote, ",");
127        assert_token_format_eq!(UnquoteSplicing, ",@");
128        assert_token_format_eq!(Num(0.0), "0");
129        assert_token_format_eq!(Num(0.5), "0.5");
130        assert_token_format_eq!(Num(1.0), "1");
131        assert_token_format_eq!(Num(123.456), "123.456");
132        assert_token_format_eq!(Num(123.456), "123.456");
133        assert_token_format_eq!(Str("str".to_string()), "\"str\"");
134        assert_token_format_eq!(Sym("sym".to_string()), "sym");
135    }
136}