1use std::fmt::{Display, Formatter, Result as FmtResult};
2
3use crate::span::{Loc, Span};
4
5#[derive(Clone, Debug)]
7pub enum Token {
8 OpenParen(Loc),
10
11 CloseParen(Loc),
13
14 Quote(Loc),
16
17 Quasiquote(Loc),
19
20 Unquote(Loc),
22
23 UnquoteSplicing(Loc),
25
26 Num(f64, Span),
28
29 Str(String, Span),
31
32 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}